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..545396704 100644 --- a/source/adt/source/adt_map.c +++ b/source/adt/source/adt_map.c @@ -251,6 +251,72 @@ vector map_get(map m, map_key key) return NULL; } +vector map_get_keys(map m) +{ + if (m != NULL) + { + vector v = vector_create(sizeof(void *)); + + if (v == NULL) + { + return NULL; + } + + for (size_t iterator = 0; iterator < m->capacity; ++iterator) + { + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) + { + size_t index; + + for (index = 0; index < b->count; ++index) + { + pair p = &b->pairs[index]; + vector_push_back(v, &p->key); + } + } + } + + return v; + } + + return NULL; +} + +vector map_get_values(map m) +{ + if (m != NULL) + { + vector v = vector_create(sizeof(void *)); + + if (v == NULL) + { + return NULL; + } + + for (size_t iterator = 0; iterator < m->capacity; ++iterator) + { + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) + { + size_t index; + + for (index = 0; index < b->count; ++index) + { + pair p = &b->pairs[index]; + vector_push_back(v, &p->value); + } + } + } + + return v; + } + + return NULL; +} + 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..9fb2c45ab 100644 --- a/source/adt/source/adt_set.c +++ b/source/adt/source/adt_set.c @@ -273,6 +273,72 @@ set_value set_get(set s, set_key key) return NULL; } +vector set_get_keys(set s) +{ + if (s != NULL) + { + vector v = vector_create(sizeof(void *)); + + if (v == NULL) + { + return NULL; + } + + for (size_t iterator = 0; iterator < s->capacity; ++iterator) + { + bucket b = &s->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) + { + size_t index; + + for (index = 0; index < b->count; ++index) + { + pair p = &b->pairs[index]; + vector_push_back(v, &p->key); + } + } + } + + return v; + } + + return NULL; +} + +vector set_get_values(set s) +{ + if (s != NULL) + { + vector v = vector_create(sizeof(void *)); + + if (v == NULL) + { + return NULL; + } + + for (size_t iterator = 0; iterator < s->capacity; ++iterator) + { + bucket b = &s->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) + { + size_t index; + + for (index = 0; index < b->count; ++index) + { + pair p = &b->pairs[index]; + vector_push_back(v, &p->value); + } + } + } + + return v; + } + + return NULL; +} + 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 2ad993732..0430f3080 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, }; @@ -287,12 +288,101 @@ 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); } } +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 +395,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 +502,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..daa348d92 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -177,6 +177,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; @@ -198,6 +204,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 { @@ -267,6 +294,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; @@ -313,6 +368,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 +426,19 @@ 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; + napi_value klass_descriptor; +}; + +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; @@ -400,6 +484,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 *); @@ -446,6 +595,20 @@ 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; + 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; + const 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 +632,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); @@ -510,8 +707,42 @@ 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); + +/* +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); @@ -812,6 +1043,280 @@ 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 > 0) + { + 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) + { + //TODO: this implementation is problematic + 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); + delete[] args; + + 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; + 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); + + 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; + 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_value jsthis; + napi_get_cb_info(env, info, NULL, NULL, &jsthis, &closure_cast.ptr); + + /* Set environment */ + closure_cast.safe->node_impl->env = env; + + bool is_constructor = target != nullptr; + + if (is_constructor) + { + const char *klass_name_str = class_name(closure_cast.safe->cls); + + 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); + } + + 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"); + } + + napi_get_cb_info(env, info, &argc, argv, NULL, NULL); + + void **args = new void *[argc]; + if (args == nullptr) + { + napi_throw_error(env, NULL, "NodeJS Loader memory allocation failed"); + } + + 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); + + if (ids != NULL) + { + ctor = class_constructor(closure_cast.safe->cls, ids, argc); + free(ids); + } + + //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]); + } + + object o = class_new(closure_cast.safe->cls, obj_name.c_str(), ctor, args, argc); + + delete[] args; + + if (o == NULL) + { + napi_throw_error(env, NULL, "NodeJS Loader Failed to create object"); + } + + 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); + + size_t argc; + status = napi_get_cb_info(env, info, &argc, 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 instance; + } +} + 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 +1464,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) */ @@ -1264,19 +1772,130 @@ 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); - napi_define_class(env, cls->name, NAPI_AUTO_LENGTH, ) - */ - } - else if (id == TYPE_OBJECT) - { + 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 = m_name; + + 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 = 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 = m_name; + + 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 = 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 = attr_name; + + 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 = 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 = attr_name; + + 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 = reinterpret_cast(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); + + status = napi_create_reference(env, result, 1, &ctor_data->constructor_ref); + 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,537 +2391,673 @@ 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; - - /* 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); + (void)obj; + (void)impl; - node_loader_impl_exception(env, status); + /* + loader_impl_node_object node_obj = (loader_impl_node_object)impl; - /* Create function string */ - status = napi_create_string_utf8(env, initialize_str, sizeof(initialize_str) - 1, &initialize_str_value); + node_obj->node_impl = NULL; + node_obj->impl = NULL;*/ - node_loader_impl_exception(env, status); + return 0; +} - /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, initialize_str_value, &result); +value node_object_interface_get(object obj, object_impl impl, struct accessor_type *accessor) +{ + loader_impl_node_object node_obj = (loader_impl_node_object)impl; + value ret = NULL; + napi_status status; - node_loader_impl_exception(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; - if (result == true) + /* Check if we are in the JavaScript thread */ + if (node_obj->node_impl->js_thread_id == std::this_thread::get_id()) { - napi_value function_trampoline_initialize; - napi_valuetype valuetype; - napi_value argv[1]; + /* 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); - status = napi_get_named_property(env, function_table_object, initialize_str, &function_trampoline_initialize); + /* 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); - node_loader_impl_exception(env, status); + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_obj->node_impl->threadsafe_object_get); - status = napi_typeof(env, function_trampoline_initialize, &valuetype); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for object set in NodeJS loader"); + } - node_loader_impl_exception(env, status); + /* 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 (valuetype != napi_function) + if (status != napi_ok) { - napi_throw_type_error(env, nullptr, "Invalid function initialize in function table object"); + log_write("metacall", LOG_LEVEL_ERROR, "call to object set thread safe function failed in NodeJS loader"); } - /* 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); + /* Release call safe function */ + status = napi_release_threadsafe_function(node_obj->node_impl->threadsafe_object_get, napi_tsfn_release); - /* Call to load from file function */ - napi_value global, return_value; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release object set thread safe function in NodeJS loader"); + } - status = napi_get_reference_value(env, node_impl->global_ref, &global); + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_obj->node_impl->cond, &node_obj->node_impl->mutex); - node_loader_impl_exception(env, status); + /* Set up return of the function call */ + ret = node_obj->node_impl->object_get_safe->ret_v; - status = napi_call_function(env, global, function_trampoline_initialize, 1, argv, &return_value); + node_obj->node_impl->locked.store(false); - node_loader_impl_exception(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"); } - /* Close scope */ - status = napi_close_handle_scope(env, handle_scope); - - node_loader_impl_exception(env, status); + return ret; } -napi_value node_loader_impl_async_initialize_safe(napi_env env, napi_callback_info info) +int node_object_interface_set(object obj, object_impl impl, struct accessor_type *accessor, value v) { - loader_impl_async_safe_cast initialize_cast = { NULL }; - - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &initialize_cast.ptr); - - node_loader_impl_exception(env, status); - - /* Lock node implementation mutex */ - uv_mutex_lock(&initialize_cast.safe->node_impl->mutex); - - /* Store environment for reentrant calls */ - initialize_cast.safe->node_impl->env = env; + loader_impl_node_object node_obj = (loader_impl_node_object)impl; + napi_status status; - /* Call to the implementation function */ - node_loader_impl_initialize_safe(env, initialize_cast.safe); + /* 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; - /* Clear environment */ - // initialize_cast.safe->node_impl->env = NULL; + /* 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); - /* Signal start condition */ - uv_cond_signal(&initialize_cast.safe->node_impl->cond); + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_obj->node_impl->threadsafe_object_set); - uv_mutex_unlock(&initialize_cast.safe->node_impl->mutex); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for object set in NodeJS loader"); + } - return nullptr; -} + /* 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); -void node_loader_impl_execution_path_safe(napi_env env, loader_impl_async_execution_path_safe execution_path_safe) -{ - static const char execution_path_str[] = "execution_path"; - napi_value function_table_object; - napi_value execution_path_str_value; - bool result = false; - napi_handle_scope handle_scope; - loader_impl_node node_impl = execution_path_safe->node_impl; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to object set thread safe function failed in NodeJS loader"); + } - /* Create scope */ - napi_status status = napi_open_handle_scope(env, &handle_scope); + /* Release call safe function */ + status = napi_release_threadsafe_function(node_obj->node_impl->threadsafe_object_set, napi_tsfn_release); - node_loader_impl_exception(env, status); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release object set thread safe function in NodeJS loader"); + } - /* Get function table object from reference */ - status = napi_get_reference_value(env, node_impl->function_table_object_ref, &function_table_object); + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_obj->node_impl->cond, &node_obj->node_impl->mutex); - node_loader_impl_exception(env, status); + node_obj->node_impl->locked.store(false); - /* Create function string */ - status = napi_create_string_utf8(env, execution_path_str, sizeof(execution_path_str) - 1, &execution_path_str_value); + /* 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"); + } - node_loader_impl_exception(env, status); + return 0; +} - /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, execution_path_str_value, &result); +value node_object_interface_method_invoke(object obj, object_impl impl, method m, object_args args, size_t argc) +{ + loader_impl_node_object node_obj = (loader_impl_node_object)impl; + value ret = NULL; + napi_status status; - node_loader_impl_exception(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; - if (result == true) + /* Check if we are in the JavaScript thread */ + if (node_obj->node_impl->js_thread_id == std::this_thread::get_id()) { - napi_value function_trampoline_execution_path; - napi_valuetype valuetype; - napi_value argv[1]; + /* 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); - status = napi_get_named_property(env, function_table_object, execution_path_str, &function_trampoline_execution_path); + /* 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); - node_loader_impl_exception(env, status); + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_obj->node_impl->threadsafe_object_method_invoke); - status = napi_typeof(env, function_trampoline_execution_path, &valuetype); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for object method invoke in NodeJS loader"); + } - node_loader_impl_exception(env, status); + /* 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); - if (valuetype != napi_function) + if (status != napi_ok) { - napi_throw_type_error(env, nullptr, "Invalid function execution_path in function table object"); + log_write("metacall", LOG_LEVEL_ERROR, "call to object method invoke thread safe function failed in NodeJS loader"); } - /* 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); + /* Release call safe function */ + status = napi_release_threadsafe_function(node_obj->node_impl->threadsafe_object_method_invoke, napi_tsfn_release); - /* Call to load from file function */ - napi_value global, return_value; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release object method invoke thread safe function in NodeJS loader"); + } - status = napi_get_reference_value(env, node_impl->global_ref, &global); + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_obj->node_impl->cond, &node_obj->node_impl->mutex); - node_loader_impl_exception(env, status); + /* Set up return of the function call */ + ret = node_obj->node_impl->object_method_invoke_safe->ret_v; - status = napi_call_function(env, global, function_trampoline_execution_path, 1, argv, &return_value); + node_obj->node_impl->locked.store(false); - node_loader_impl_exception(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_method_invoke, the call has not been executed in order to avoid the deadlock"); } - /* Close scope */ - status = napi_close_handle_scope(env, handle_scope); + return ret; +} - node_loader_impl_exception(env, status); +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; } -napi_value node_loader_impl_async_execution_path_safe(napi_env env, napi_callback_info info) +int node_object_interface_destructor(object obj, object_impl impl) { - loader_impl_async_safe_cast execution_path_cast = { NULL }; + (void)obj; + (void)impl; - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &execution_path_cast.ptr); + /* Destructors are automatically called when ref count is zero and GC happens */ - node_loader_impl_exception(env, status); + return 0; +} - /* Lock node implementation mutex */ - uv_mutex_lock(&execution_path_cast.safe->node_impl->mutex); +void node_object_interface_destroy(object obj, object_impl impl) +{ + loader_impl_node_object node_obj = (loader_impl_node_object)impl; - /* Store environment for reentrant calls */ - execution_path_cast.safe->node_impl->env = env; + if (node_obj != NULL) + { + if (loader_is_destroyed(node_obj->impl) != 0) + { + loader_impl_node node_impl = node_obj->node_impl; + napi_status status; - /* Call to the implementation function */ - node_loader_impl_execution_path_safe(env, execution_path_cast.safe); + /* 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; - /* Clear environment */ - // execution_path_cast.safe->node_impl->env = NULL; + /* 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); - /* Signal start condition */ - uv_cond_signal(&execution_path_cast.safe->node_impl->cond); + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_impl->threadsafe_object_destroy); - uv_mutex_unlock(&execution_path_cast.safe->node_impl->mutex); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function class_destroy in NodeJS loader"); + } - return nullptr; -} + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_impl->threadsafe_object_destroy, nullptr, napi_tsfn_nonblocking); -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); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to class_destroy_safe thread safe function failed in NodeJS loader"); + } - /* Get function data */ - args_size = func_call_safe->size; - node_func = func_call_safe->node_func; - args = func_call_safe->args; + /* Release call safe function */ + status = napi_release_threadsafe_function(node_impl->threadsafe_object_destroy, napi_tsfn_release); - /* 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; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release class_destroy_safe thread safe function in NodeJS loader"); + } - /* Create scope */ - napi_status status = napi_open_handle_scope(env, &handle_scope); + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_impl->cond, &node_impl->mutex); - node_loader_impl_exception(env, status); + node_impl->locked.store(false); - /* 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]); - } + /* 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"); + } + } - /* Get function reference */ - napi_value function_ptr; + /* Delete node class impl */ + delete node_obj; + } +} - status = napi_get_reference_value(env, node_func->func_ref, &function_ptr); +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 + }; - node_loader_impl_exception(env, status); + return &node_object_interface; +} - /* Get global */ - napi_value global; +int node_class_interface_create(klass cls, class_impl impl) +{ + (void)cls; + (void)impl; - status = napi_get_reference_value(env, func_call_safe->node_impl->global_ref, &global); + return 0; +} - node_loader_impl_exception(env, status); +object node_class_interface_constructor(klass cls, class_impl impl, const char *name, constructor ctor, class_args args, size_t argc) +{ + (void)ctor; - /* Call to function */ - napi_value func_return; + loader_impl_node_class node_klass = (loader_impl_node_class)impl; - status = napi_call_function(env, global, function_ptr, args_size, argv, &func_return); + object ret = nullptr; + napi_status status; - func_call_safe->ret = node_loader_impl_exception_value(func_call_safe->node_impl, env, status, func_call_safe->recv); + /* 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 (func_call_safe->ret == NULL) + /* Check if we are in the JavaScript thread */ + if (node_klass->node_impl->js_thread_id == std::this_thread::get_id()) { - /* 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); + /* 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); - if (args_size > signature_args_size) - { - free(argv); + /* 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 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; + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_klass->node_impl->threadsafe_class_ctor); - status = napi_get_cb_info(env, info, nullptr, nullptr, &recv, &func_call_cast.ptr); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for class constructor call in NodeJS loader"); + } - node_loader_impl_exception(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); - /* Lock the call safe mutex and get the parameters */ - uv_mutex_lock(&func_call_cast.safe->node_impl->mutex); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to class constructor thread safe function failed in NodeJS loader"); + } - /* Store function recv for reentrant calls */ - func_call_cast.safe->recv = recv; + /* Release call safe function */ + status = napi_release_threadsafe_function(node_klass->node_impl->threadsafe_class_ctor, napi_tsfn_release); - /* Store environment for reentrant calls */ - func_call_cast.safe->node_impl->env = env; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release class constructor thread safe function in NodeJS loader"); + } - /* Call to the implementation function */ - node_loader_impl_func_call_safe(env, func_call_cast.safe); + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_klass->node_impl->cond, &node_klass->node_impl->mutex); - /* Clear environment */ - // func_call_cast.safe->node_impl->env = NULL; + /* Set up return of the function call */ + ret = node_klass->node_impl->class_ctor_safe->ret_obj; - /* Signal function call condition */ - uv_cond_signal(&func_call_cast.safe->node_impl->cond); + node_klass->node_impl->locked.store(false); - uv_mutex_unlock(&func_call_cast.safe->node_impl->mutex); + /* 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 nullptr; + return ret; } -void node_loader_impl_async_func_await_finalize(napi_env, void *finalize_data, void *) +value node_class_interface_static_get(klass cls, class_impl impl, struct accessor_type *accessor) { - loader_impl_async_func_await_trampoline trampoline = static_cast(finalize_data); + (void)cls; + (void)impl; + (void)accessor; - free(trampoline); + return NULL; } -napi_value node_loader_impl_async_func_resolve(loader_impl_node node_impl, napi_env env, function_resolve_callback resolve, napi_value recv, napi_value v, void *context) +int node_class_interface_static_set(klass cls, class_impl impl, struct accessor_type *accessor, value v) { - napi_value result; - value arg, ret; - - if (node_impl == NULL || resolve == NULL) - { - return nullptr; - } - - /* Convert the argument to a value */ - arg = node_loader_impl_napi_to_value(node_impl, env, recv, v); + (void)cls; + (void)impl; + (void)accessor; + (void)v; - if (arg == NULL) - { - arg = value_create_null(); - } + return 0; +} - /* Call the resolve callback */ - ret = resolve(arg, context); +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; + value ret = NULL; + napi_status status; - /* Destroy parameter argument */ - value_type_destroy(arg); + /* 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; - /* Return the result */ - if (ret != NULL) + /* Check if we are in the JavaScript thread */ + if (node_klass->node_impl->js_thread_id == std::this_thread::get_id()) { - result = node_loader_impl_value_to_napi(node_impl, env, ret); + /* 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); + + /* Set up return of the static method call */ + ret = node_klass->node_impl->class_static_invoke_safe->ret_v; } - else + /* 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) { - napi_status status = napi_get_undefined(env, &result); + node_klass->node_impl->locked.store(true); - node_loader_impl_exception(env, status); - } + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_klass->node_impl->threadsafe_class_static_invoke); - /* Destroy return value */ - value_type_destroy(ret); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for static method invoke in NodeJS loader"); + } - return result; -} + /* 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); -napi_value node_loader_impl_async_func_reject(loader_impl_node node_impl, napi_env env, function_reject_callback reject, napi_value recv, napi_value v, void *context) -{ - napi_value result; - value arg, ret; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to static method invoke thread safe function failed in NodeJS loader"); + } - if (node_impl == NULL || reject == NULL) - { - return nullptr; - } + /* Release call safe function */ + status = napi_release_threadsafe_function(node_klass->node_impl->threadsafe_class_static_invoke, napi_tsfn_release); - /* Convert the argument to a value */ - arg = node_loader_impl_napi_to_value(node_impl, env, recv, v); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release static method invoke thread safe function in NodeJS loader"); + } - if (arg == NULL) - { - arg = value_create_null(); - } + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_klass->node_impl->cond, &node_klass->node_impl->mutex); - /* Call the reject callback */ - ret = reject(arg, context); + /* Set up return of the function call */ + ret = node_klass->node_impl->class_static_invoke_safe->ret_v; - /* Destroy parameter argument */ - value_type_destroy(arg); + node_klass->node_impl->locked.store(false); - /* Return the result */ - if (ret != NULL) - { - result = node_loader_impl_value_to_napi(node_impl, env, ret); + /* Unlock the mutex */ + uv_mutex_unlock(&node_klass->node_impl->mutex); } else { - napi_status status = napi_get_undefined(env, &result); - - node_loader_impl_exception(env, status); + 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"); } - /* Destroy return value */ - value_type_destroy(ret); - - return result; + return ret; } -void node_loader_impl_func_await_safe(napi_env env, loader_impl_async_func_await_safe func_await_safe) +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 const char await_str[] = "await_function"; - napi_value await_str_value; - napi_value function_table_object; - napi_value function_await; - bool result = false; - napi_value argv[3]; - napi_handle_scope handle_scope; + // Class methods in JavaScript can't be async + (void)cls; + (void)impl; + (void)m; + (void)args; + (void)size; + (void)resolve; + (void)reject; + (void)ctx; - /* Create scope */ - napi_status status = napi_open_handle_scope(env, &handle_scope); + return NULL; +} - node_loader_impl_exception(env, status); +void node_class_interface_destroy(klass cls, class_impl impl) +{ + loader_impl_node_class node_klass = (loader_impl_node_class)impl; - /* Get function table object from reference */ - status = napi_get_reference_value(env, func_await_safe->node_impl->function_table_object_ref, &function_table_object); + if (node_klass != NULL) + { + if (loader_is_destroyed(node_klass->impl) != 0) + { + loader_impl_node node_impl = node_klass->node_impl; + napi_status status; - node_loader_impl_exception(env, 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; - /* Retrieve resolve function from object table */ - status = napi_create_string_utf8(env, await_str, sizeof(await_str) - 1, &await_str_value); + /* 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); - node_loader_impl_exception(env, status); + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_impl->threadsafe_class_destroy); - status = napi_has_own_property(env, function_table_object, await_str_value, &result); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function class_destroy in NodeJS loader"); + } - node_loader_impl_exception(env, status); + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_impl->threadsafe_class_destroy, nullptr, napi_tsfn_nonblocking); - if (result == true) - { - napi_valuetype valuetype; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to class_destroy_safe thread safe function failed in NodeJS loader"); + } - status = napi_get_named_property(env, function_table_object, await_str, &function_await); + /* Release call safe function */ + status = napi_release_threadsafe_function(node_impl->threadsafe_class_destroy, napi_tsfn_release); - node_loader_impl_exception(env, status); - - status = napi_typeof(env, function_await, &valuetype); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release class_destroy_safe thread safe function in NodeJS loader"); + } - node_loader_impl_exception(env, status); + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_impl->cond, &node_impl->mutex); - if (valuetype != napi_function) - { - napi_throw_type_error(env, nullptr, "Invalid function test in function table object"); - } - else - { - /* Allocate trampoline object */ - loader_impl_async_func_await_trampoline trampoline = static_cast(malloc(sizeof(struct loader_impl_async_func_await_trampoline_type))); + node_impl->locked.store(false); - if (trampoline != NULL) + /* Unlock call safe mutex */ + uv_mutex_unlock(&node_impl->mutex); + } + else { - napi_ref trampoline_ref; - size_t args_size; - value *args; - loader_impl_node_function node_func; - size_t args_count; - napi_value *func_argv; - signature s = function_signature(func_await_safe->func); - const size_t signature_args_size = signature_count(s); - - /* Get function reference */ - status = napi_get_reference_value(env, func_await_safe->node_func->func_ref, &argv[0]); + 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"); + } + } - node_loader_impl_exception(env, status); + /* Delete node class impl */ + delete node_klass; + } +} - /* Create array for arguments */ - status = napi_create_array(env, &argv[1]); +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 + }; - node_loader_impl_exception(env, status); + return &node_class_interface; +} - /* Get push property from array */ - napi_value push_func; +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; - status = napi_get_named_property(env, argv[1], "push", &push_func); + /* Create scope */ + napi_status status = napi_open_handle_scope(env, &handle_scope); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - /* Get function data */ - args_size = func_await_safe->size; - args = static_cast(func_await_safe->args); - node_func = func_await_safe->node_func; + /* Get function table object from reference */ + status = napi_get_reference_value(env, node_impl->function_table_object_ref, &function_table_object); - /* Allocate dynamically more space for values in case of variable arguments */ - func_argv = args_size > signature_args_size ? static_cast(malloc(sizeof(napi_value) * args_size)) : node_func->argv; + node_loader_impl_exception(env, status); - /* Build parameters */ - for (args_count = 0; args_count < args_size; ++args_count) - { - /* Define parameter */ - func_argv[args_count] = node_loader_impl_value_to_napi(func_await_safe->node_impl, env, args[args_count]); + /* Create function string */ + status = napi_create_string_utf8(env, initialize_str, sizeof(initialize_str) - 1, &initialize_str_value); - /* Push parameter to the array */ - status = napi_call_function(env, argv[1], push_func, 1, &func_argv[args_count], NULL); + node_loader_impl_exception(env, status); - 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); - /* Set trampoline object values */ - trampoline->node_loader = func_await_safe->node_impl; - trampoline->resolve_trampoline = &node_loader_impl_async_func_resolve; - trampoline->reject_trampoline = &node_loader_impl_async_func_reject; - trampoline->resolve_callback = func_await_safe->resolve_callback; - trampoline->reject_callback = func_await_safe->reject_callback; - trampoline->context = func_await_safe->context; + node_loader_impl_exception(env, status); - /* Set the C trampoline object as JS wrapped object */ - status = napi_create_object(env, &argv[2]); + if (result == true) + { + napi_value function_trampoline_initialize; + napi_valuetype valuetype; + napi_value argv[1]; - node_loader_impl_exception(env, status); + status = napi_get_named_property(env, function_table_object, initialize_str, &function_trampoline_initialize); - status = napi_wrap(env, argv[2], static_cast(trampoline), &node_loader_impl_async_func_await_finalize, NULL, &trampoline_ref); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + status = napi_typeof(env, function_trampoline_initialize, &valuetype); - /* Call to function */ - napi_value global, await_return; + node_loader_impl_exception(env, status); - status = napi_get_reference_value(env, func_await_safe->node_impl->global_ref, &global); + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid function initialize in function table object"); + } - node_loader_impl_exception(env, status); + /* Create parameters */ + status = napi_create_string_utf8(env, initialize_safe->loader_library_path, strlen(initialize_safe->loader_library_path), &argv[0]); - status = napi_call_function(env, global, function_await, 3, argv, &await_return); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Call to load from file function */ + napi_value global, return_value; - /* Delete references references to wrapped objects */ - status = napi_delete_reference(env, trampoline_ref); + status = napi_get_reference_value(env, node_impl->global_ref, &global); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - /* Proccess the await return */ - func_await_safe->ret = node_loader_impl_napi_to_value(func_await_safe->node_impl, env, func_await_safe->recv, await_return); + status = napi_call_function(env, global, function_trampoline_initialize, 1, argv, &return_value); - if (args_size > signature_args_size) - { - free(func_argv); - } - } - } + node_loader_impl_exception(env, status); } /* Close scope */ @@ -2311,233 +3066,385 @@ void node_loader_impl_func_await_safe(napi_env env, loader_impl_async_func_await node_loader_impl_exception(env, status); } -napi_value node_loader_impl_async_func_await_safe(napi_env env, napi_callback_info info) +napi_value node_loader_impl_async_initialize_safe(napi_env env, napi_callback_info info) { - napi_value recv; - loader_impl_async_safe_cast func_await_cast = { NULL }; + loader_impl_async_safe_cast initialize_cast = { NULL }; - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &recv, &func_await_cast.ptr); + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &initialize_cast.ptr); node_loader_impl_exception(env, status); /* Lock node implementation mutex */ - uv_mutex_lock(&func_await_cast.safe->node_impl->mutex); - - /* Store function recv for reentrant calls */ - func_await_cast.safe->recv = recv; + uv_mutex_lock(&initialize_cast.safe->node_impl->mutex); /* Store environment for reentrant calls */ - func_await_cast.safe->node_impl->env = env; + initialize_cast.safe->node_impl->env = env; /* Call to the implementation function */ - node_loader_impl_func_await_safe(env, func_await_cast.safe); + node_loader_impl_initialize_safe(env, initialize_cast.safe); /* Clear environment */ - // func_await_cast.safe->node_impl->env = NULL; + // initialize_cast.safe->node_impl->env = NULL; - /* Signal function await condition */ - uv_cond_signal(&func_await_cast.safe->node_impl->cond); + /* Signal start condition */ + uv_cond_signal(&initialize_cast.safe->node_impl->cond); - uv_mutex_unlock(&func_await_cast.safe->node_impl->mutex); + uv_mutex_unlock(&initialize_cast.safe->node_impl->mutex); return nullptr; } -void node_loader_impl_func_destroy_safe(napi_env env, loader_impl_async_func_destroy_safe func_destroy_safe) +void node_loader_impl_execution_path_safe(napi_env env, loader_impl_async_execution_path_safe execution_path_safe) { - uint32_t ref_count = 0; + static const char execution_path_str[] = "execution_path"; + napi_value function_table_object; + napi_value execution_path_str_value; + bool result = false; napi_handle_scope handle_scope; + loader_impl_node node_impl = execution_path_safe->node_impl; /* Create scope */ napi_status status = napi_open_handle_scope(env, &handle_scope); node_loader_impl_exception(env, status); - /* Clear function persistent reference */ - status = napi_reference_unref(env, func_destroy_safe->node_func->func_ref, &ref_count); + /* 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); - if (ref_count != 0) - { - /* TODO: Error handling */ - } + /* Create function string */ + status = napi_create_string_utf8(env, execution_path_str, sizeof(execution_path_str) - 1, &execution_path_str_value); - status = napi_delete_reference(env, func_destroy_safe->node_func->func_ref); + node_loader_impl_exception(env, status); + + /* Check if exists in the table */ + status = napi_has_own_property(env, function_table_object, execution_path_str_value, &result); node_loader_impl_exception(env, status); + if (result == true) + { + napi_value function_trampoline_execution_path; + napi_valuetype valuetype; + napi_value argv[1]; + + 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_func_destroy_safe(napi_env env, napi_callback_info info) +napi_value node_loader_impl_async_execution_path_safe(napi_env env, napi_callback_info info) { - loader_impl_async_safe_cast func_destroy_cast = { NULL }; + loader_impl_async_safe_cast execution_path_cast = { NULL }; - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &func_destroy_cast.ptr); + 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(&func_destroy_cast.safe->node_impl->mutex); + uv_mutex_lock(&execution_path_cast.safe->node_impl->mutex); /* Store environment for reentrant calls */ - func_destroy_cast.safe->node_impl->env = env; + execution_path_cast.safe->node_impl->env = env; /* Call to the implementation function */ - node_loader_impl_func_destroy_safe(env, func_destroy_cast.safe); + node_loader_impl_execution_path_safe(env, execution_path_cast.safe); /* Clear environment */ - // func_destroy_cast.safe->node_impl->env = NULL; + // execution_path_cast.safe->node_impl->env = NULL; - /* Signal function destroy condition */ - uv_cond_signal(&func_destroy_cast.safe->node_impl->cond); + /* Signal start condition */ + uv_cond_signal(&execution_path_cast.safe->node_impl->cond); - uv_mutex_unlock(&func_destroy_cast.safe->node_impl->mutex); + uv_mutex_unlock(&execution_path_cast.safe->node_impl->mutex); return nullptr; } -void node_loader_impl_future_await_safe(napi_env env, loader_impl_async_future_await_safe future_await_safe) +void node_loader_impl_func_call_safe(napi_env env, loader_impl_async_func_call_safe func_call_safe) { - static const char await_str[] = "await_future"; - napi_value await_str_value; - napi_value function_table_object; - napi_value future_await; - bool result = false; - napi_value argv[2]; 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); - /* Create scope */ - napi_status status = napi_open_handle_scope(env, &handle_scope); - - node_loader_impl_exception(env, status); + /* Get function data */ + args_size = func_call_safe->size; + node_func = func_call_safe->node_func; + args = func_call_safe->args; - /* Get function table object from reference */ - status = napi_get_reference_value(env, future_await_safe->node_impl->function_table_object_ref, &function_table_object); + /* 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); - /* Retrieve resolve function from object table */ - status = napi_create_string_utf8(env, await_str, sizeof(await_str) - 1, &await_str_value); + /* 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); - status = napi_has_own_property(env, function_table_object, await_str_value, &result); + /* 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); - if (result == true) - { - napi_valuetype valuetype; + /* Call to function */ + napi_value func_return; - status = napi_get_named_property(env, function_table_object, await_str, &future_await); + status = napi_call_function(env, global, function_ptr, args_size, argv, &func_return); - node_loader_impl_exception(env, status); + func_call_safe->ret = node_loader_impl_exception_value(func_call_safe->node_impl, env, status, func_call_safe->recv); - status = napi_typeof(env, future_await, &valuetype); + 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); + } - node_loader_impl_exception(env, status); + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); - if (valuetype != napi_function) - { - napi_throw_type_error(env, nullptr, "Invalid function await_future in function table object"); - } - else - { - /* Allocate trampoline object */ - loader_impl_async_func_await_trampoline trampoline = static_cast(malloc(sizeof(struct loader_impl_async_func_await_trampoline_type))); + node_loader_impl_exception(env, status); - if (trampoline != NULL) - { - napi_ref trampoline_ref; + if (args_size > signature_args_size) + { + free(argv); + } +} - /* Get function reference */ - status = napi_get_reference_value(env, future_await_safe->node_future->promise_ref, &argv[0]); +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; - node_loader_impl_exception(env, status); + status = napi_get_cb_info(env, info, nullptr, nullptr, &recv, &func_call_cast.ptr); - /* Set trampoline object values */ - trampoline->node_loader = future_await_safe->node_impl; - trampoline->resolve_trampoline = &node_loader_impl_async_func_resolve; - trampoline->reject_trampoline = &node_loader_impl_async_func_reject; - trampoline->resolve_callback = future_await_safe->resolve_callback; - trampoline->reject_callback = future_await_safe->reject_callback; - trampoline->context = future_await_safe->context; + node_loader_impl_exception(env, status); - /* Set the C trampoline object as JS wrapped object */ - status = napi_create_object(env, &argv[1]); + /* Lock the call safe mutex and get the parameters */ + uv_mutex_lock(&func_call_cast.safe->node_impl->mutex); - node_loader_impl_exception(env, status); + /* Store function recv for reentrant calls */ + func_call_cast.safe->recv = recv; - status = napi_wrap(env, argv[1], static_cast(trampoline), &node_loader_impl_async_func_await_finalize, NULL, &trampoline_ref); + /* Store environment for reentrant calls */ + func_call_cast.safe->node_impl->env = env; - node_loader_impl_exception(env, status); + /* Call to the implementation function */ + node_loader_impl_func_call_safe(env, func_call_cast.safe); - /* Call to function */ - napi_value global, await_return; + /* Clear environment */ + // func_call_cast.safe->node_impl->env = NULL; - status = napi_get_reference_value(env, future_await_safe->node_impl->global_ref, &global); + /* Signal function call condition */ + uv_cond_signal(&func_call_cast.safe->node_impl->cond); - node_loader_impl_exception(env, status); + uv_mutex_unlock(&func_call_cast.safe->node_impl->mutex); - status = napi_call_function(env, global, future_await, 2, argv, &await_return); + return nullptr; +} - node_loader_impl_exception(env, status); +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; - /* Delete references references to wrapped objects */ - status = napi_delete_reference(env, trampoline_ref); + status = napi_get_reference_value(env, object_method_invoke_safe->node_obj->obj_ref, &obj_value); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + const char *m_name = method_name(object_method_invoke_safe->m); - /* Proccess the await return */ - future_await_safe->ret = node_loader_impl_napi_to_value(future_await_safe->node_impl, env, future_await_safe->recv, await_return); - } - } + 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]); } - /* Close scope */ - status = napi_close_handle_scope(env, handle_scope); + 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; } -napi_value node_loader_impl_async_future_await_safe(napi_env env, napi_callback_info info) +void node_loader_impl_object_get_safe(napi_env env, loader_impl_async_object_get_safe object_get_safe) { - napi_value recv; - loader_impl_async_safe_cast future_await_cast = { NULL }; + napi_value obj_value = NULL; + napi_status status; - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &recv, &future_await_cast.ptr); + 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); - /* Lock node implementation mutex */ - uv_mutex_lock(&future_await_cast.safe->node_impl->mutex); + value v = node_loader_impl_napi_to_value(object_get_safe->node_impl, env, nullptr, prop_value); - /* Store function recv for reentrant calls */ - future_await_cast.safe->recv = recv; + 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 */ - future_await_cast.safe->node_impl->env = env; + object_get_cast.safe->node_impl->env = env; /* Call to the implementation function */ - node_loader_impl_future_await_safe(env, future_await_cast.safe); + node_loader_impl_object_get_safe(env, object_get_cast.safe); - /* Clear environment */ - // future_await_cast.safe->node_impl->env = NULL; + /* Signal function call condition */ + uv_cond_signal(&object_get_cast.safe->node_impl->cond); - /* Signal function await condition */ - uv_cond_signal(&future_await_cast.safe->node_impl->cond); + uv_mutex_unlock(&object_get_cast.safe->node_impl->mutex); - uv_mutex_unlock(&future_await_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_future_delete_safe(napi_env env, loader_impl_async_future_delete_safe future_delete_safe) +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; @@ -2547,8 +3454,8 @@ void node_loader_impl_future_delete_safe(napi_env env, loader_impl_async_future_ node_loader_impl_exception(env, status); - /* Clear promise reference */ - status = napi_reference_unref(env, future_delete_safe->node_future->promise_ref, &ref_count); + /* Clear class persistent reference */ + status = napi_reference_unref(env, object_destroy_safe->node_obj->obj_ref, &ref_count); node_loader_impl_exception(env, status); @@ -2557,7 +3464,7 @@ void node_loader_impl_future_delete_safe(napi_env env, loader_impl_async_future_ /* TODO: Error handling */ } - status = napi_delete_reference(env, future_delete_safe->node_future->promise_ref); + status = napi_delete_reference(env, object_destroy_safe->node_obj->obj_ref); node_loader_impl_exception(env, status); @@ -2567,168 +3474,182 @@ void node_loader_impl_future_delete_safe(napi_env env, loader_impl_async_future_ node_loader_impl_exception(env, status); } -napi_value node_loader_impl_async_future_delete_safe(napi_env env, napi_callback_info info) +napi_value node_loader_impl_async_object_destroy_safe(napi_env env, napi_callback_info info) { - loader_impl_async_safe_cast future_delete_cast = { NULL }; + loader_impl_async_safe_cast object_destroy_safe_cast = { NULL }; - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &future_delete_cast.ptr); + 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(&future_delete_cast.safe->node_impl->mutex); + uv_mutex_lock(&object_destroy_safe_cast.safe->node_impl->mutex); /* Store environment for reentrant calls */ - future_delete_cast.safe->node_impl->env = env; + object_destroy_safe_cast.safe->node_impl->env = env; /* Call to the implementation function */ - node_loader_impl_future_delete_safe(env, future_delete_cast.safe); - - /* Clear environment */ - // future_delete_cast.safe->node_impl->env = NULL; + node_loader_impl_object_destroy_safe(env, object_destroy_safe_cast.safe); - /* Signal future delete condition */ - uv_cond_signal(&future_delete_cast.safe->node_impl->cond); + /* Signal function destroy condition */ + uv_cond_signal(&object_destroy_safe_cast.safe->node_impl->cond); - uv_mutex_unlock(&future_delete_cast.safe->node_impl->mutex); + uv_mutex_unlock(&object_destroy_safe_cast.safe->node_impl->mutex); return nullptr; } -void node_loader_impl_load_from_file_safe(napi_env env, loader_impl_async_load_from_file_safe load_from_file_safe) +void node_loader_impl_class_static_invoke_safe(napi_env env, loader_impl_async_class_static_invoke_safe class_static_invoke_safe) { - static const char load_from_file_str[] = "load_from_file"; - napi_value function_table_object; - napi_value load_from_file_str_value; - bool result = false; + napi_status status; napi_handle_scope handle_scope; /* Create scope */ - napi_status status = napi_open_handle_scope(env, &handle_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); - /* Get function table object from reference */ - status = napi_get_reference_value(env, load_from_file_safe->node_impl->function_table_object_ref, &function_table_object); + 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); - /* Create function string */ - status = napi_create_string_utf8(env, load_from_file_str, sizeof(load_from_file_str) - 1, &load_from_file_str_value); - - node_loader_impl_exception(env, status); + napi_value *m_args = new napi_value[class_static_invoke_safe->argc]; - /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, load_from_file_str_value, &result); + 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); - if (result == true) - { - napi_value function_trampoline_load_from_file; - napi_valuetype valuetype; - napi_value argv[1]; + value v = node_loader_impl_napi_to_value(class_static_invoke_safe->node_impl, env, nullptr, result); - status = napi_get_named_property(env, function_table_object, load_from_file_str, &function_trampoline_load_from_file); + delete[] m_args; - node_loader_impl_exception(env, status); + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); - status = napi_typeof(env, function_trampoline_load_from_file, &valuetype); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + class_static_invoke_safe->ret_v = v; +} - if (valuetype != napi_function) - { - napi_throw_type_error(env, nullptr, "Invalid function load_from_file in function table object"); - } +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; - /* Define parameters */ - status = napi_create_array_with_length(env, load_from_file_safe->size, &argv[0]); + status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &class_static_invoke_cast.ptr); - node_loader_impl_exception(env, status); - - for (size_t index = 0; index < load_from_file_safe->size; ++index) - { - napi_value path_str; + node_loader_impl_exception(env, status); - size_t length = strnlen(load_from_file_safe->paths[index], LOADER_PATH_SIZE); + /* Lock the call safe mutex and get the parameters */ + uv_mutex_lock(&class_static_invoke_cast.safe->node_impl->mutex); - status = napi_create_string_utf8(env, load_from_file_safe->paths[index], length, &path_str); + /* Store environment for reentrant calls */ + class_static_invoke_cast.safe->node_impl->env = env; - node_loader_impl_exception(env, status); + /* Call to the implementation function */ + node_loader_impl_class_static_invoke_safe(env, class_static_invoke_cast.safe); - status = napi_set_element(env, argv[0], (uint32_t)index, path_str); + /* Signal function call condition */ + uv_cond_signal(&class_static_invoke_cast.safe->node_impl->cond); - node_loader_impl_exception(env, status); - } + uv_mutex_unlock(&class_static_invoke_cast.safe->node_impl->mutex); - /* Call to load from file function */ - napi_value global, return_value; + return nullptr; +} - status = napi_get_reference_value(env, load_from_file_safe->node_impl->global_ref, &global); +void node_loader_impl_class_ctor_safe(napi_env env, loader_impl_async_class_ctor_safe class_ctor_safe) +{ + napi_handle_scope handle_scope; - node_loader_impl_exception(env, status); + /* Create scope */ + napi_status status = napi_open_handle_scope(env, &handle_scope); - status = napi_call_function(env, global, function_trampoline_load_from_file, 1, argv, &return_value); + 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; - /* Check return value */ - napi_valuetype return_valuetype; + object obj = object_create(class_ctor_safe->name, ACCESSOR_TYPE_DYNAMIC, node_obj, &node_object_interface_singleton, class_ctor_safe->cls); - status = napi_typeof(env, return_value, &return_valuetype); + if (obj == NULL) + { + class_ctor_safe->ret_obj = NULL; + return; + } - node_loader_impl_exception(env, status); + /* Get loader implementation from class */ + node_obj->node_impl = class_ctor_safe->node_impl; + //node_obj->impl = node_klass->impl; - if (return_valuetype != napi_null) - { - /* Make handle persistent */ - status = napi_create_reference(env, return_value, 1, &load_from_file_safe->handle_ref); + 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]); } + 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); + + napi_value result; + status = napi_new_instance(env, klass_cons, class_ctor_safe->argc, m_args, &result); + node_loader_impl_exception(env, status); + + //Todo check that object is an instance of constructor + + status = napi_create_reference(env, result, 1, &node_obj->obj_ref); + 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_load_from_file_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 load_from_file_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, &load_from_file_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(&load_from_file_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 */ - load_from_file_cast.safe->node_impl->env = env; + class_ctor_safe_cast.safe->node_impl->env = env; /* Call to the implementation function */ - node_loader_impl_load_from_file_safe(env, load_from_file_cast.safe); - - /* Clear environment */ - // load_from_file_cast.safe->node_impl->env = NULL; + node_loader_impl_class_ctor_safe(env, class_ctor_safe_cast.safe); - /* Signal load from file condition */ - uv_cond_signal(&load_from_file_cast.safe->node_impl->cond); + /* Signal function call condition */ + uv_cond_signal(&class_ctor_safe_cast.safe->node_impl->cond); - uv_mutex_unlock(&load_from_file_cast.safe->node_impl->mutex); + uv_mutex_unlock(&class_ctor_safe_cast.safe->node_impl->mutex); return nullptr; } -void node_loader_impl_load_from_memory_safe(napi_env env, loader_impl_async_load_from_memory_safe load_from_memory_safe) +void node_loader_impl_class_destroy_safe(napi_env env, loader_impl_async_class_destroy_safe class_destroy_safe) { - static const char load_from_memory_str[] = "load_from_memory"; - napi_value function_table_object; - napi_value load_from_memory_str_value; - bool result = false; + uint32_t ref_count = 0; napi_handle_scope handle_scope; /* Create scope */ @@ -2736,180 +3657,334 @@ void node_loader_impl_load_from_memory_safe(napi_env env, loader_impl_async_load node_loader_impl_exception(env, status); - /* Get function table object from reference */ - status = napi_get_reference_value(env, load_from_memory_safe->node_impl->function_table_object_ref, &function_table_object); + /* Clear class persistent reference */ + status = napi_reference_unref(env, class_destroy_safe->node_klass->klass_ref, &ref_count); node_loader_impl_exception(env, status); - /* Create function string */ - status = napi_create_string_utf8(env, load_from_memory_str, sizeof(load_from_memory_str) - 1, &load_from_memory_str_value); + if (ref_count != 0) + { + /* TODO: Error handling */ + } + + status = napi_delete_reference(env, class_destroy_safe->node_klass->klass_ref); node_loader_impl_exception(env, status); - /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, load_from_memory_str_value, &result); + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); node_loader_impl_exception(env, status); +} - if (result == true) - { - napi_value function_trampoline_load_from_memory; - napi_valuetype valuetype; - napi_value argv[3]; - - status = napi_get_named_property(env, function_table_object, load_from_memory_str, &function_trampoline_load_from_memory); +napi_value node_loader_impl_async_class_destroy_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast class_destroy_safe_cast = { NULL }; - node_loader_impl_exception(env, status); + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &class_destroy_safe_cast.ptr); - status = napi_typeof(env, function_trampoline_load_from_memory, &valuetype); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Lock node implementation mutex */ + uv_mutex_lock(&class_destroy_safe_cast.safe->node_impl->mutex); - if (valuetype != napi_function) - { - napi_throw_type_error(env, nullptr, "Invalid function load_from_memory in function table object"); - } + /* Store environment for reentrant calls */ + class_destroy_safe_cast.safe->node_impl->env = env; - /* Define parameters */ - status = napi_create_string_utf8(env, load_from_memory_safe->name, strlen(load_from_memory_safe->name), &argv[0]); + /* Call to the implementation function */ + node_loader_impl_class_destroy_safe(env, class_destroy_safe_cast.safe); - node_loader_impl_exception(env, status); + /* Signal function destroy condition */ + uv_cond_signal(&class_destroy_safe_cast.safe->node_impl->cond); - status = napi_create_string_utf8(env, load_from_memory_safe->buffer, load_from_memory_safe->size - 1, &argv[1]); + uv_mutex_unlock(&class_destroy_safe_cast.safe->node_impl->mutex); - node_loader_impl_exception(env, status); + return nullptr; +} - status = napi_create_object(env, &argv[2]); +void node_loader_impl_async_func_await_finalize(napi_env, void *finalize_data, void *) +{ + loader_impl_async_func_await_trampoline trampoline = static_cast(finalize_data); - node_loader_impl_exception(env, status); + free(trampoline); +} - /* Call to load from memory function */ - napi_value global, return_value; +napi_value node_loader_impl_async_func_resolve(loader_impl_node node_impl, napi_env env, function_resolve_callback resolve, napi_value recv, napi_value v, void *context) +{ + napi_value result; + value arg, ret; - status = napi_get_reference_value(env, load_from_memory_safe->node_impl->global_ref, &global); + if (node_impl == NULL || resolve == NULL) + { + return nullptr; + } - node_loader_impl_exception(env, status); + /* Convert the argument to a value */ + arg = node_loader_impl_napi_to_value(node_impl, env, recv, v); - status = napi_call_function(env, global, function_trampoline_load_from_memory, 3, argv, &return_value); + if (arg == NULL) + { + arg = value_create_null(); + } - node_loader_impl_exception(env, status); + /* Call the resolve callback */ + ret = resolve(arg, context); - /* Check return value */ - napi_valuetype return_valuetype; + /* Destroy parameter argument */ + value_type_destroy(arg); - status = napi_typeof(env, return_value, &return_valuetype); + /* Return the result */ + if (ret != NULL) + { + result = node_loader_impl_value_to_napi(node_impl, env, ret); + } + else + { + napi_status status = napi_get_undefined(env, &result); node_loader_impl_exception(env, status); - - if (return_valuetype != napi_null) - { - /* Make handle persistent */ - status = napi_create_reference(env, return_value, 1, &load_from_memory_safe->handle_ref); - - node_loader_impl_exception(env, status); - } } - /* Close scope */ - status = napi_close_handle_scope(env, handle_scope); + /* Destroy return value */ + value_type_destroy(ret); - node_loader_impl_exception(env, status); + return result; } -napi_value node_loader_impl_async_load_from_memory_safe(napi_env env, napi_callback_info info) +napi_value node_loader_impl_async_func_reject(loader_impl_node node_impl, napi_env env, function_reject_callback reject, napi_value recv, napi_value v, void *context) { - loader_impl_async_safe_cast load_from_memory_cast = { NULL }; + napi_value result; + value arg, ret; - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &load_from_memory_cast.ptr); + if (node_impl == NULL || reject == NULL) + { + return nullptr; + } - node_loader_impl_exception(env, status); + /* Convert the argument to a value */ + arg = node_loader_impl_napi_to_value(node_impl, env, recv, v); - /* Lock node implementation mutex */ - uv_mutex_lock(&load_from_memory_cast.safe->node_impl->mutex); + if (arg == NULL) + { + arg = value_create_null(); + } - /* Store environment for reentrant calls */ - load_from_memory_cast.safe->node_impl->env = env; + /* Call the reject callback */ + ret = reject(arg, context); - /* Call to the implementation function */ - node_loader_impl_load_from_memory_safe(env, load_from_memory_cast.safe); + /* Destroy parameter argument */ + value_type_destroy(arg); - /* Clear environment */ - // load_from_memory_cast.safe->node_impl->env = NULL; + /* Return the result */ + if (ret != NULL) + { + result = node_loader_impl_value_to_napi(node_impl, env, ret); + } + else + { + napi_status status = napi_get_undefined(env, &result); - /* Signal load from memory condition */ - uv_cond_signal(&load_from_memory_cast.safe->node_impl->cond); + node_loader_impl_exception(env, status); + } - uv_mutex_unlock(&load_from_memory_cast.safe->node_impl->mutex); + /* Destroy return value */ + value_type_destroy(ret); - return nullptr; + return result; } -void node_loader_impl_clear_safe(napi_env env, loader_impl_async_clear_safe clear_safe) +void node_loader_impl_func_await_safe(napi_env env, loader_impl_async_func_await_safe func_await_safe) { - static const char clear_str[] = "clear"; + static const char await_str[] = "await_function"; + napi_value await_str_value; napi_value function_table_object; - napi_value clear_str_value; + napi_value function_await; bool result = false; + napi_value argv[3]; napi_handle_scope handle_scope; - uint32_t ref_count = 0; /* 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, clear_safe->node_impl->function_table_object_ref, &function_table_object); + status = napi_get_reference_value(env, func_await_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, clear_str, sizeof(clear_str) - 1, &clear_str_value); + /* Retrieve resolve function from object table */ + status = napi_create_string_utf8(env, await_str, sizeof(await_str) - 1, &await_str_value); node_loader_impl_exception(env, status); - /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, clear_str_value, &result); + status = napi_has_own_property(env, function_table_object, await_str_value, &result); node_loader_impl_exception(env, status); if (result == true) { - napi_value function_trampoline_clear; napi_valuetype valuetype; - napi_value argv[1]; - status = napi_get_named_property(env, function_table_object, clear_str, &function_trampoline_clear); + status = napi_get_named_property(env, function_table_object, await_str, &function_await); node_loader_impl_exception(env, status); - status = napi_typeof(env, function_trampoline_clear, &valuetype); + status = napi_typeof(env, function_await, &valuetype); node_loader_impl_exception(env, status); if (valuetype != napi_function) { - napi_throw_type_error(env, nullptr, "Invalid function clear in function table object"); + napi_throw_type_error(env, nullptr, "Invalid function test in function table object"); } + else + { + /* Allocate trampoline object */ + loader_impl_async_func_await_trampoline trampoline = static_cast(malloc(sizeof(struct loader_impl_async_func_await_trampoline_type))); - /* Define parameters */ - status = napi_get_reference_value(env, clear_safe->handle_ref, &argv[0]); - - node_loader_impl_exception(env, status); - - /* Call to load from file function */ - napi_value global, clear_return; + if (trampoline != NULL) + { + napi_ref trampoline_ref; + size_t args_size; + value *args; + loader_impl_node_function node_func; + size_t args_count; + napi_value *func_argv; + signature s = function_signature(func_await_safe->func); + const size_t signature_args_size = signature_count(s); - status = napi_get_reference_value(env, clear_safe->node_impl->global_ref, &global); + /* Get function reference */ + status = napi_get_reference_value(env, func_await_safe->node_func->func_ref, &argv[0]); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - status = napi_call_function(env, global, function_trampoline_clear, 1, argv, &clear_return); + /* Create array for arguments */ + status = napi_create_array(env, &argv[1]); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); + + /* Get push property from array */ + napi_value push_func; + + status = napi_get_named_property(env, argv[1], "push", &push_func); + + node_loader_impl_exception(env, status); + + /* Get function data */ + args_size = func_await_safe->size; + args = static_cast(func_await_safe->args); + node_func = func_await_safe->node_func; + + /* Allocate dynamically more space for values in case of variable arguments */ + func_argv = args_size > signature_args_size ? static_cast(malloc(sizeof(napi_value) * args_size)) : node_func->argv; + + /* Build parameters */ + for (args_count = 0; args_count < args_size; ++args_count) + { + /* Define parameter */ + func_argv[args_count] = node_loader_impl_value_to_napi(func_await_safe->node_impl, env, args[args_count]); + + /* Push parameter to the array */ + status = napi_call_function(env, argv[1], push_func, 1, &func_argv[args_count], NULL); + + node_loader_impl_exception(env, status); + } + + /* Set trampoline object values */ + trampoline->node_loader = func_await_safe->node_impl; + trampoline->resolve_trampoline = &node_loader_impl_async_func_resolve; + trampoline->reject_trampoline = &node_loader_impl_async_func_reject; + trampoline->resolve_callback = func_await_safe->resolve_callback; + trampoline->reject_callback = func_await_safe->reject_callback; + trampoline->context = func_await_safe->context; + + /* Set the C trampoline object as JS wrapped object */ + status = napi_create_object(env, &argv[2]); + + node_loader_impl_exception(env, status); + + status = napi_wrap(env, argv[2], static_cast(trampoline), &node_loader_impl_async_func_await_finalize, NULL, &trampoline_ref); + + node_loader_impl_exception(env, status); + + /* Call to function */ + napi_value global, await_return; + + status = napi_get_reference_value(env, func_await_safe->node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + status = napi_call_function(env, global, function_await, 3, argv, &await_return); + + node_loader_impl_exception(env, status); + + /* Delete references references to wrapped objects */ + status = napi_delete_reference(env, trampoline_ref); + + node_loader_impl_exception(env, status); + + /* Proccess the await return */ + func_await_safe->ret = node_loader_impl_napi_to_value(func_await_safe->node_impl, env, func_await_safe->recv, await_return); + + if (args_size > signature_args_size) + { + free(func_argv); + } + } + } } - /* Clear handle persistent reference */ - status = napi_reference_unref(env, clear_safe->handle_ref, &ref_count); + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); + + node_loader_impl_exception(env, status); +} + +napi_value node_loader_impl_async_func_await_safe(napi_env env, napi_callback_info info) +{ + napi_value recv; + loader_impl_async_safe_cast func_await_cast = { NULL }; + + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &recv, &func_await_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock node implementation mutex */ + uv_mutex_lock(&func_await_cast.safe->node_impl->mutex); + + /* Store function recv for reentrant calls */ + func_await_cast.safe->recv = recv; + + /* Store environment for reentrant calls */ + func_await_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_func_await_safe(env, func_await_cast.safe); + + /* Clear environment */ + // func_await_cast.safe->node_impl->env = NULL; + + /* Signal function await condition */ + uv_cond_signal(&func_await_cast.safe->node_impl->cond); + + uv_mutex_unlock(&func_await_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_func_destroy_safe(napi_env env, loader_impl_async_func_destroy_safe func_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 function persistent reference */ + status = napi_reference_unref(env, func_destroy_safe->node_func->func_ref, &ref_count); node_loader_impl_exception(env, status); @@ -2918,7 +3993,7 @@ void node_loader_impl_clear_safe(napi_env env, loader_impl_async_clear_safe clea /* TODO: Error handling */ } - status = napi_delete_reference(env, clear_safe->handle_ref); + status = napi_delete_reference(env, func_destroy_safe->node_func->func_ref); node_loader_impl_exception(env, status); @@ -2928,42 +4003,43 @@ void node_loader_impl_clear_safe(napi_env env, loader_impl_async_clear_safe clea node_loader_impl_exception(env, status); } -napi_value node_loader_impl_async_clear_safe(napi_env env, napi_callback_info info) +napi_value node_loader_impl_async_func_destroy_safe(napi_env env, napi_callback_info info) { - loader_impl_async_safe_cast clear_cast = { NULL }; + loader_impl_async_safe_cast func_destroy_cast = { NULL }; - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &clear_cast.ptr); + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &func_destroy_cast.ptr); node_loader_impl_exception(env, status); /* Lock node implementation mutex */ - uv_mutex_lock(&clear_cast.safe->node_impl->mutex); + uv_mutex_lock(&func_destroy_cast.safe->node_impl->mutex); /* Store environment for reentrant calls */ - clear_cast.safe->node_impl->env = env; + func_destroy_cast.safe->node_impl->env = env; /* Call to the implementation function */ - node_loader_impl_clear_safe(env, clear_cast.safe); + node_loader_impl_func_destroy_safe(env, func_destroy_cast.safe); /* Clear environment */ - // clear_cast.safe->node_impl->env = NULL; + // func_destroy_cast.safe->node_impl->env = NULL; - /* Signal clear condition */ - uv_cond_signal(&clear_cast.safe->node_impl->cond); + /* Signal function destroy condition */ + uv_cond_signal(&func_destroy_cast.safe->node_impl->cond); - uv_mutex_unlock(&clear_cast.safe->node_impl->mutex); + uv_mutex_unlock(&func_destroy_cast.safe->node_impl->mutex); return nullptr; } -value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_discover_function_safe discover_function_safe) +void node_loader_impl_future_await_safe(napi_env env, loader_impl_async_future_await_safe future_await_safe) { - static const char discover_function_str[] = "discover_function"; - napi_value discover_function_str_value; + static const char await_str[] = "await_future"; + napi_value await_str_value; napi_value function_table_object; + napi_value future_await; bool result = false; + napi_value argv[2]; napi_handle_scope handle_scope; - value function_value = NULL; /* Create scope */ napi_status status = napi_open_handle_scope(env, &handle_scope); @@ -2971,319 +4047,1307 @@ value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_di node_loader_impl_exception(env, status); /* Get function table object from reference */ - status = napi_get_reference_value(env, discover_function_safe->node_impl->function_table_object_ref, &function_table_object); + status = napi_get_reference_value(env, future_await_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_function_str, sizeof(discover_function_str) - 1, &discover_function_str_value); + /* Retrieve resolve function from object table */ + status = napi_create_string_utf8(env, await_str, sizeof(await_str) - 1, &await_str_value); node_loader_impl_exception(env, status); - /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, discover_function_str_value, &result); + status = napi_has_own_property(env, function_table_object, await_str_value, &result); node_loader_impl_exception(env, status); if (result == true) { - napi_value function_trampoline_discover; napi_valuetype valuetype; - napi_value argv[1]; - status = napi_get_named_property(env, function_table_object, discover_function_str, &function_trampoline_discover); + status = napi_get_named_property(env, function_table_object, await_str, &future_await); node_loader_impl_exception(env, status); - status = napi_typeof(env, function_trampoline_discover, &valuetype); + status = napi_typeof(env, future_await, &valuetype); node_loader_impl_exception(env, status); if (valuetype != napi_function) { - napi_throw_type_error(env, nullptr, "Invalid function discover in function table object"); + napi_throw_type_error(env, nullptr, "Invalid function await_future in function table object"); + } + else + { + /* Allocate trampoline object */ + loader_impl_async_func_await_trampoline trampoline = static_cast(malloc(sizeof(struct loader_impl_async_func_await_trampoline_type))); + + if (trampoline != NULL) + { + napi_ref trampoline_ref; + + /* Get function reference */ + status = napi_get_reference_value(env, future_await_safe->node_future->promise_ref, &argv[0]); + + node_loader_impl_exception(env, status); + + /* Set trampoline object values */ + trampoline->node_loader = future_await_safe->node_impl; + trampoline->resolve_trampoline = &node_loader_impl_async_func_resolve; + trampoline->reject_trampoline = &node_loader_impl_async_func_reject; + trampoline->resolve_callback = future_await_safe->resolve_callback; + trampoline->reject_callback = future_await_safe->reject_callback; + trampoline->context = future_await_safe->context; + + /* Set the C trampoline object as JS wrapped object */ + status = napi_create_object(env, &argv[1]); + + node_loader_impl_exception(env, status); + + status = napi_wrap(env, argv[1], static_cast(trampoline), &node_loader_impl_async_func_await_finalize, NULL, &trampoline_ref); + + node_loader_impl_exception(env, status); + + /* Call to function */ + napi_value global, await_return; + + status = napi_get_reference_value(env, future_await_safe->node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + status = napi_call_function(env, global, future_await, 2, argv, &await_return); + + node_loader_impl_exception(env, status); + + /* Delete references references to wrapped objects */ + status = napi_delete_reference(env, trampoline_ref); + + node_loader_impl_exception(env, status); + + /* Proccess the await return */ + future_await_safe->ret = node_loader_impl_napi_to_value(future_await_safe->node_impl, env, future_await_safe->recv, await_return); + } } + } - /* Define parameters */ - argv[0] = discover_function_safe->func; + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); - /* Call to load from file function */ - napi_value global, function_descriptor; + node_loader_impl_exception(env, status); +} + +napi_value node_loader_impl_async_future_await_safe(napi_env env, napi_callback_info info) +{ + napi_value recv; + loader_impl_async_safe_cast future_await_cast = { NULL }; + + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &recv, &future_await_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock node implementation mutex */ + uv_mutex_lock(&future_await_cast.safe->node_impl->mutex); + + /* Store function recv for reentrant calls */ + future_await_cast.safe->recv = recv; + + /* Store environment for reentrant calls */ + future_await_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_future_await_safe(env, future_await_cast.safe); + + /* Clear environment */ + // future_await_cast.safe->node_impl->env = NULL; + + /* Signal function await condition */ + uv_cond_signal(&future_await_cast.safe->node_impl->cond); + + uv_mutex_unlock(&future_await_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_future_delete_safe(napi_env env, loader_impl_async_future_delete_safe future_delete_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 promise reference */ + status = napi_reference_unref(env, future_delete_safe->node_future->promise_ref, &ref_count); + + node_loader_impl_exception(env, status); + + if (ref_count != 0) + { + /* TODO: Error handling */ + } + + status = napi_delete_reference(env, future_delete_safe->node_future->promise_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_future_delete_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast future_delete_cast = { NULL }; + + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &future_delete_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock node implementation mutex */ + uv_mutex_lock(&future_delete_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + future_delete_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_future_delete_safe(env, future_delete_cast.safe); + + /* Clear environment */ + // future_delete_cast.safe->node_impl->env = NULL; + + /* Signal future delete condition */ + uv_cond_signal(&future_delete_cast.safe->node_impl->cond); + + uv_mutex_unlock(&future_delete_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_load_from_file_safe(napi_env env, loader_impl_async_load_from_file_safe load_from_file_safe) +{ + static const char load_from_file_str[] = "load_from_file"; + napi_value function_table_object; + napi_value load_from_file_str_value; + bool result = false; + napi_handle_scope handle_scope; + + /* 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, load_from_file_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, load_from_file_str, sizeof(load_from_file_str) - 1, &load_from_file_str_value); + + node_loader_impl_exception(env, status); + + /* Check if exists in the table */ + status = napi_has_own_property(env, function_table_object, load_from_file_str_value, &result); + + node_loader_impl_exception(env, status); + + if (result == true) + { + napi_value function_trampoline_load_from_file; + napi_valuetype valuetype; + napi_value argv[1]; + + status = napi_get_named_property(env, function_table_object, load_from_file_str, &function_trampoline_load_from_file); + + node_loader_impl_exception(env, status); + + status = napi_typeof(env, function_trampoline_load_from_file, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid function load_from_file in function table object"); + } + + /* Define parameters */ + status = napi_create_array_with_length(env, load_from_file_safe->size, &argv[0]); + + node_loader_impl_exception(env, status); + + for (size_t index = 0; index < load_from_file_safe->size; ++index) + { + napi_value path_str; + + size_t length = strnlen(load_from_file_safe->paths[index], LOADER_PATH_SIZE); + + status = napi_create_string_utf8(env, load_from_file_safe->paths[index], length, &path_str); + + node_loader_impl_exception(env, status); + + status = napi_set_element(env, argv[0], (uint32_t)index, path_str); + + node_loader_impl_exception(env, status); + } + + /* Call to load from file function */ + napi_value global, return_value; + + status = napi_get_reference_value(env, load_from_file_safe->node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + status = napi_call_function(env, global, function_trampoline_load_from_file, 1, argv, &return_value); + + node_loader_impl_exception(env, status); + + /* Check return value */ + napi_valuetype return_valuetype; + + status = napi_typeof(env, return_value, &return_valuetype); + + node_loader_impl_exception(env, status); + + if (return_valuetype != napi_null) + { + /* Make handle persistent */ + status = napi_create_reference(env, return_value, 1, &load_from_file_safe->handle_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_load_from_file_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast load_from_file_cast = { NULL }; + + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &load_from_file_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock node implementation mutex */ + uv_mutex_lock(&load_from_file_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + load_from_file_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_load_from_file_safe(env, load_from_file_cast.safe); + + /* Clear environment */ + // load_from_file_cast.safe->node_impl->env = NULL; + + /* Signal load from file condition */ + uv_cond_signal(&load_from_file_cast.safe->node_impl->cond); + + uv_mutex_unlock(&load_from_file_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_load_from_memory_safe(napi_env env, loader_impl_async_load_from_memory_safe load_from_memory_safe) +{ + static const char load_from_memory_str[] = "load_from_memory"; + napi_value function_table_object; + napi_value load_from_memory_str_value; + bool result = false; + napi_handle_scope handle_scope; + + /* 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, load_from_memory_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, load_from_memory_str, sizeof(load_from_memory_str) - 1, &load_from_memory_str_value); + + node_loader_impl_exception(env, status); + + /* Check if exists in the table */ + status = napi_has_own_property(env, function_table_object, load_from_memory_str_value, &result); + + node_loader_impl_exception(env, status); + + if (result == true) + { + napi_value function_trampoline_load_from_memory; + napi_valuetype valuetype; + napi_value argv[3]; + + status = napi_get_named_property(env, function_table_object, load_from_memory_str, &function_trampoline_load_from_memory); + + node_loader_impl_exception(env, status); + + status = napi_typeof(env, function_trampoline_load_from_memory, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid function load_from_memory in function table object"); + } + + /* Define parameters */ + status = napi_create_string_utf8(env, load_from_memory_safe->name, strlen(load_from_memory_safe->name), &argv[0]); + + node_loader_impl_exception(env, status); + + status = napi_create_string_utf8(env, load_from_memory_safe->buffer, load_from_memory_safe->size - 1, &argv[1]); + + node_loader_impl_exception(env, status); + + status = napi_create_object(env, &argv[2]); + + node_loader_impl_exception(env, status); + + /* Call to load from memory function */ + napi_value global, return_value; + + status = napi_get_reference_value(env, load_from_memory_safe->node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + status = napi_call_function(env, global, function_trampoline_load_from_memory, 3, argv, &return_value); + + node_loader_impl_exception(env, status); + + /* Check return value */ + napi_valuetype return_valuetype; + + status = napi_typeof(env, return_value, &return_valuetype); + + node_loader_impl_exception(env, status); + + if (return_valuetype != napi_null) + { + /* Make handle persistent */ + status = napi_create_reference(env, return_value, 1, &load_from_memory_safe->handle_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_load_from_memory_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast load_from_memory_cast = { NULL }; + + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &load_from_memory_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock node implementation mutex */ + uv_mutex_lock(&load_from_memory_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + load_from_memory_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_load_from_memory_safe(env, load_from_memory_cast.safe); + + /* Clear environment */ + // load_from_memory_cast.safe->node_impl->env = NULL; + + /* Signal load from memory condition */ + uv_cond_signal(&load_from_memory_cast.safe->node_impl->cond); + + uv_mutex_unlock(&load_from_memory_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_clear_safe(napi_env env, loader_impl_async_clear_safe clear_safe) +{ + static const char clear_str[] = "clear"; + napi_value function_table_object; + napi_value clear_str_value; + bool result = false; + napi_handle_scope handle_scope; + uint32_t ref_count = 0; + + /* 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, clear_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, clear_str, sizeof(clear_str) - 1, &clear_str_value); + + node_loader_impl_exception(env, status); + + /* Check if exists in the table */ + status = napi_has_own_property(env, function_table_object, clear_str_value, &result); + + node_loader_impl_exception(env, status); + + if (result == true) + { + napi_value function_trampoline_clear; + napi_valuetype valuetype; + napi_value argv[1]; + + status = napi_get_named_property(env, function_table_object, clear_str, &function_trampoline_clear); + + node_loader_impl_exception(env, status); + + status = napi_typeof(env, function_trampoline_clear, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid function clear in function table object"); + } + + /* Define parameters */ + status = napi_get_reference_value(env, clear_safe->handle_ref, &argv[0]); + + node_loader_impl_exception(env, status); + + /* Call to load from file function */ + napi_value global, clear_return; + + status = napi_get_reference_value(env, clear_safe->node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + status = napi_call_function(env, global, function_trampoline_clear, 1, argv, &clear_return); + + node_loader_impl_exception(env, status); + } + + /* Clear handle persistent reference */ + status = napi_reference_unref(env, clear_safe->handle_ref, &ref_count); + + node_loader_impl_exception(env, status); + + if (ref_count != 0) + { + /* TODO: Error handling */ + } + + status = napi_delete_reference(env, clear_safe->handle_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_clear_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast clear_cast = { NULL }; + + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &clear_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock node implementation mutex */ + uv_mutex_lock(&clear_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + clear_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_clear_safe(env, clear_cast.safe); + + /* Clear environment */ + // clear_cast.safe->node_impl->env = NULL; + + /* Signal clear condition */ + uv_cond_signal(&clear_cast.safe->node_impl->cond); + + uv_mutex_unlock(&clear_cast.safe->node_impl->mutex); + + return nullptr; +} + +value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_discover_function_safe discover_function_safe) +{ + static const char discover_function_str[] = "discover_function"; + napi_value discover_function_str_value; + napi_value function_table_object; + bool result = false; + napi_handle_scope handle_scope; + value function_value = NULL; + + /* 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_function_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_function_str, sizeof(discover_function_str) - 1, &discover_function_str_value); + + node_loader_impl_exception(env, status); + + /* Check if exists in the table */ + status = napi_has_own_property(env, function_table_object, discover_function_str_value, &result); + + node_loader_impl_exception(env, status); + + if (result == true) + { + napi_value function_trampoline_discover; + napi_valuetype valuetype; + napi_value argv[1]; + + status = napi_get_named_property(env, function_table_object, discover_function_str, &function_trampoline_discover); + + node_loader_impl_exception(env, status); + + status = napi_typeof(env, function_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_function_safe->func; + + /* Call to load from file function */ + napi_value global, function_descriptor; + + status = napi_get_reference_value(env, discover_function_safe->node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + status = napi_call_function(env, global, function_trampoline_discover, 1, argv, &function_descriptor); + + 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; + bool has_name = false; + + status = napi_has_named_property(env, function_descriptor, "name", &has_name); + + node_loader_impl_exception(env, status); + + /* Retrieve the function name if any */ + if (has_name == true) + { + size_t func_name_length = 0; + + 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) +{ + napi_handle_scope handle_scope; + klass c = NULL; + constructor ctor = NULL; + value klass_value = NULL; + int ret = 1; - status = napi_get_reference_value(env, discover_function_safe->node_impl->global_ref, &global); + /* Create scope */ + napi_status status = napi_open_handle_scope(env, &handle_scope); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - status = napi_call_function(env, global, function_trampoline_discover, 1, argv, &function_descriptor); + /* 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); + node_loader_impl_exception(env, status); - /* Convert return value (discover object) to context */ - napi_value func_name; - char *func_name_str = NULL; + if (is_klass == true) + { + napi_value klass_name; + char *klass_name_str = NULL; bool has_name = false; - status = napi_has_named_property(env, function_descriptor, "name", &has_name); + status = napi_has_named_property(env, discover_klass_safe->klass_descriptor, "name", &has_name); node_loader_impl_exception(env, status); - /* Retrieve the function name if any */ + /* Retrieve the class name if any */ if (has_name == true) { - size_t func_name_length = 0; + size_t klass_name_length = 0; - status = napi_get_named_property(env, function_descriptor, "name", &func_name); + status = napi_get_named_property(env, discover_klass_safe->klass_descriptor, "name", &klass_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, klass_name, NULL, 0, &klass_name_length); node_loader_impl_exception(env, status); - if (func_name_length > 0) + if (klass_name_length > 0) { - func_name_str = static_cast(malloc(sizeof(char) * (func_name_length + 1))); + klass_name_str = static_cast(malloc(sizeof(char) * (klass_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); + /* 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); } - /* 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; + loader_impl_node_class node_klass = new loader_impl_node_class_type(); - /* Get function signature */ - status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); + node_klass->node_impl = discover_klass_safe->node_impl; - node_loader_impl_exception(env, status); + status = napi_create_reference(env, discover_klass_safe->klass, 1, &node_klass->klass_ref); - /* Check function pointer type */ - status = napi_typeof(env, function_sig, &valuetype); + 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, discover_klass_safe->klass_descriptor, "methods", &has_methods); node_loader_impl_exception(env, status); - if (valuetype != napi_object) + if (has_methods == true) { - napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); - } + status = napi_get_named_property(env, discover_klass_safe->klass_descriptor, "methods", &methods); - /* 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); + /* Discover constructor */ + napi_value klass_constructor; + bool has_constructor = false; - /* Get function async */ - status = napi_get_named_property(env, function_descriptor, "async", &function_is_async); + status = napi_has_named_property(env, methods, "klass_constructor", &has_constructor); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - /* Check function async type */ - status = napi_typeof(env, function_is_async, &valuetype); + if (has_constructor == true) + { + status = napi_get_named_property(env, methods, "klass_constructor", &klass_constructor); - 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"); - } + napi_value constructor_sig; + bool has_sig = false; - /* 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, klass_constructor, "signature", &has_sig); - status = napi_has_named_property(env, function_descriptor, types_str, &has_types); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + if (has_sig == true) + { + status = napi_get_named_property(env, klass_constructor, "signature", &constructor_sig); - if (has_types == true) - { - status = napi_get_named_property(env, function_descriptor, types_str, &function_types); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + uint32_t constructor_sig_length = 0; + napi_valuetype valuetype; - /* Check types array type */ - status = napi_typeof(env, function_types, &valuetype); + /* Check signature type */ + status = napi_typeof(env, constructor_sig, &valuetype); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (valuetype != napi_object) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS function types"); - } - } + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); + } - /* 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; + /* Get signature length */ + status = napi_get_array_length(env, constructor_sig, &constructor_sig_length); - status = napi_has_named_property(env, function_descriptor, ret_str, &has_ret); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + ctor = constructor_create(constructor_sig_length, VISIBILITY_PUBLIC); - if (has_ret == true) - { - status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); + /* 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; - node_loader_impl_exception(env, status); + /* Get signature parameter name */ + status = napi_get_element(env, constructor_sig, arg_index, ¶meter_name); - /* Check return value type */ - status = napi_typeof(env, function_ret, &valuetype); + node_loader_impl_exception(env, status); - 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); - if (valuetype != napi_string) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); - } - } + node_loader_impl_exception(env, status); - /* Create node function */ - loader_impl_node_function node_func = static_cast(malloc(sizeof(struct loader_impl_node_function_type))); + if (parameter_name_length > 0) + { + parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); + } - /* Create reference to function pointer */ - status = napi_create_reference(env, discover_function_safe->func, 1, &node_func->func_ref); + /* 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); - node_func->node_impl = discover_function_safe->node_impl; - node_func->impl = discover_function_safe->node_impl->impl; + /* Todo: Discover type info */ - /* Create function */ - function f = function_create(func_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); + constructor_set(ctor, (size_t)arg_index, parameter_name_str, NULL); - if (f != NULL) - { - signature s = function_signature(f); - bool is_async = false; + if (parameter_name_str != NULL) + { + free(parameter_name_str); + } + } + } + ret = class_register_constructor(c, ctor); - /* Set function async */ - status = napi_get_value_bool(env, function_is_async, &is_async); + 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); + + if (ret != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Failed to register constructor in class %s", class_name(c)); + } + } + + /* Discover methods */ + napi_value method_names; + uint32_t method_names_length; + + status = napi_get_property_names(env, methods, &method_names); node_loader_impl_exception(env, status); - function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); + status = napi_get_array_length(env, method_names, &method_names_length); - /* Set return value if any */ - if (has_ret) + node_loader_impl_exception(env, status); + + for (uint32_t index = 0; index < method_names_length; ++index) { - size_t return_type_length; - char *return_type_str = NULL; + napi_value method_name; + napi_value method_descriptor; + size_t method_name_length; + char *method_name_str = NULL; - /* Get return value string length */ - status = napi_get_value_string_utf8(env, function_ret, NULL, 0, &return_type_length); + status = napi_get_element(env, method_names, index, &method_name); node_loader_impl_exception(env, status); - if (return_type_length > 0) + status = napi_get_value_string_utf8(env, method_name, NULL, 0, &method_name_length); + + node_loader_impl_exception(env, status); + + if (method_name_length > 0) { - return_type_str = static_cast(malloc(sizeof(char) * (return_type_length + 1))); + method_name_str = static_cast(malloc(sizeof(char) * (method_name_length + 1))); } - if (return_type_str != NULL) + if (method_name_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); + return method_name_str; + } + + /* Get function name */ + status = napi_get_value_string_utf8(env, method_name, method_name_str, method_name_length + 1, &method_name_length); + + if (strcmp(method_name_str, "klass_constructor") == 0) + { + continue; + } + + node_loader_impl_exception(env, status); + + /* Get method descriptor */ + status = napi_get_named_property(env, methods, method_name_str, &method_descriptor); + + node_loader_impl_exception(env, status); + + bool has_static = false; + napi_value static_value; + bool is_static_method = false; + napi_value method_sig; + bool has_sig = false; + + status = napi_has_named_property(env, method_descriptor, "signature", &has_sig); + + node_loader_impl_exception(env, status); + + if (has_sig == true) + { + status = napi_get_named_property(env, method_descriptor, "signature", &method_sig); 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); + uint32_t method_sig_length = 0; + napi_valuetype valuetype; - free(return_type_str); + /* Check signature type */ + status = napi_typeof(env, method_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, method_sig, &method_sig_length); + + node_loader_impl_exception(env, status); + + method m = method_create(c, method_name_str, method_sig_length, NULL, VISIBILITY_PUBLIC, SYNCHRONOUS, NULL); + + status = napi_has_named_property(env, method_descriptor, "static", &has_static); + + node_loader_impl_exception(env, status); + + if (has_static != false) + { + status = napi_get_named_property(env, method_descriptor, "static", &static_value); + + node_loader_impl_exception(env, status); + + status = napi_get_value_bool(env, static_value, &is_static_method); + + node_loader_impl_exception(env, status); + } + + if (is_static_method) + { + class_register_static_method(c, m); + } + else + { + class_register_method(c, m); + } + + 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; + + /* Get signature parameter name */ + status = napi_get_element(env, method_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); + + /* Todo: Discover type info? */ + signature_set(s, (size_t)arg_index, parameter_name_str, NULL); + + if (parameter_name_str != NULL) + { + free(parameter_name_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, discover_klass_safe->klass_descriptor, "attributes", &has_attr); + + node_loader_impl_exception(env, status); + + if (has_attr == true) + { + status = napi_get_named_property(env, discover_klass_safe->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); - } + klass_value = value_create_class(c); } /* Close scope */ @@ -3291,13 +5355,19 @@ value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_di node_loader_impl_exception(env, status); - return function_value; + return klass_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) { 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; @@ -3308,7 +5378,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); @@ -3318,21 +5388,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); @@ -3353,306 +5423,361 @@ 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); - /* Get function pointer */ - status = napi_get_named_property(env, function_descriptor, "ptr", &function_ptr); + 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, property_descriptor, "func", &is_func); node_loader_impl_exception(env, status); - if (valuetype != napi_function) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS function"); - } - - /* Get function signature */ - status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); - - node_loader_impl_exception(env, status); + bool is_klass = false; - /* Check function pointer type */ - status = napi_typeof(env, function_sig, &valuetype); + status = napi_has_named_property(env, property_descriptor, "klass", &is_klass); node_loader_impl_exception(env, status); - if (valuetype != napi_object) + /* Check if a function pointer exists */ + if (is_func == true) { - 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); + 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; - /* Check function async type */ - status = napi_typeof(env, function_is_async, &valuetype); + /* Get function pointer */ + status = napi_get_named_property(env, property_descriptor, "func", &function_ptr); - node_loader_impl_exception(env, status); - - 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 function pointer type */ + status = napi_typeof(env, function_ptr, &valuetype); - status = napi_has_named_property(env, function_descriptor, types_str, &has_types); + 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 (has_types == true) - { - status = napi_get_named_property(env, function_descriptor, types_str, &function_types); + /* Get function signature */ + status = napi_get_named_property(env, property_descriptor, "signature", &function_sig); node_loader_impl_exception(env, status); - /* Check types array type */ - status = napi_typeof(env, function_types, &valuetype); + /* 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 function types"); + napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); } - } - - /* 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); + /* 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 (has_ret == true) - { - status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); + /* Get function async */ + status = napi_get_named_property(env, property_descriptor, "async", &function_is_async); node_loader_impl_exception(env, status); - /* Check return value type */ - status = napi_typeof(env, function_ret, &valuetype); + /* Check function async type */ + status = napi_typeof(env, function_is_async, &valuetype); node_loader_impl_exception(env, status); - if (valuetype != napi_string) + if (valuetype != napi_boolean) { - napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); + napi_throw_type_error(env, nullptr, "Invalid NodeJS async flag"); } - } - /* Create node function */ - loader_impl_node_function node_func = static_cast(malloc(sizeof(struct loader_impl_node_function_type))); + /* 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; - /* Create reference to function pointer */ - status = napi_create_reference(env, function_ptr, 1, &node_func->func_ref); + status = napi_has_named_property(env, property_descriptor, types_str, &has_types); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); + + if (has_types == true) + { + status = napi_get_named_property(env, property_descriptor, types_str, &function_types); - 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); + /* Check types array type */ + status = napi_typeof(env, function_types, &valuetype); - if (f != NULL) - { - signature s = function_signature(f); - scope sp = context_scope(discover_safe->ctx); - bool is_async = false; + node_loader_impl_exception(env, status); + + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS function types"); + } + } - /* Set function async */ - status = napi_get_value_bool(env, function_is_async, &is_async); + /* 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, property_descriptor, ret_str, &has_ret); - function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); + node_loader_impl_exception(env, status); - /* Set return value if any */ - if (has_ret) + if (has_ret == true) { - size_t return_type_length; - char *return_type_str = NULL; + status = napi_get_named_property(env, property_descriptor, ret_str, &function_ret); + + node_loader_impl_exception(env, status); - /* Get return value string length */ - status = napi_get_value_string_utf8(env, function_ret, NULL, 0, &return_type_length); + /* Check return value type */ + status = napi_typeof(env, function_ret, &valuetype); node_loader_impl_exception(env, status); - if (return_type_length > 0) + if (valuetype != napi_string) { - return_type_str = static_cast(malloc(sizeof(char) * (return_type_length + 1))); + napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); } + } - 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); + /* 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, function_ptr, 1, &node_func->func_ref); + + node_loader_impl_exception(env, status); - signature_set_return(s, loader_impl_type(discover_safe->node_impl->impl, return_type_str)); + node_func->node_impl = discover_safe->node_impl; + node_func->impl = discover_safe->node_impl->impl; - free(return_type_str); - } - } + /* Create function */ + function f = function_create(prop_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); - /* Set signature */ - for (uint32_t arg_index = 0; arg_index < function_sig_length; ++arg_index) + if (f != NULL) { - napi_value parameter_name; - size_t parameter_name_length; - char *parameter_name_str = NULL; + signature s = function_signature(f); + scope sp = context_scope(discover_safe->ctx); + bool is_async = false; - /* Get signature parameter name */ - status = napi_get_element(env, function_sig, arg_index, ¶meter_name); + /* Set function async */ + status = napi_get_value_bool(env, function_is_async, &is_async); 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); + function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); - if (parameter_name_length > 0) + /* Set return value if any */ + if (has_ret) { - parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); - } + size_t return_type_length; + char *return_type_str = NULL; - /* Get parameter name string */ - status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶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 (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); - /* Check if type info is available */ - if (has_types) + node_loader_impl_exception(env, status); + + signature_set_return(s, loader_impl_type(discover_safe->node_impl->impl, return_type_str)); + + free(return_type_str); + } + } + + /* 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); - if (parameter_type_str != NULL) + 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); + + 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 + else if (is_klass == true) { - free(node_func); - discover_safe->result = 1; - break; + 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, + property_descriptor + }; + + 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); } } } @@ -3948,6 +6073,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"; @@ -5177,6 +7393,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); @@ -5353,6 +7618,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 f30a518a6..8bc64b6be 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_get_methods(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_get_attributes(klass cls); + +REFLECT_API vector class_get_static_attributes(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..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,6 +680,16 @@ 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_get_methods(klass cls) +{ + return map_get_values(cls->methods); +} + +vector class_get_static_methods(klass cls) +{ + return map_get_values(cls->static_methods); +} + attribute class_static_attribute(klass cls, const char *key) { if (cls == NULL || key == NULL) @@ -700,6 +710,16 @@ attribute class_attribute(klass cls, const char *key) return set_get(cls->attributes, (set_key)key); } +vector class_get_attributes(klass cls) +{ + return set_get_values(cls->attributes); +} + +vector class_get_static_attributes(klass cls) +{ + return set_get_values(cls->static_attributes); +} + 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"; 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..9b1f86eed --- /dev/null +++ b/source/scripts/node/node_test/source/node_test.js @@ -0,0 +1,73 @@ +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!"); + } + + 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 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/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..06f69316a --- /dev/null +++ b/source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp @@ -0,0 +1,239 @@ +/* + * 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/node_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); + + { //Invoke static method + + { + const 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 *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); + } + + { + void *args[] = { + metacall_value_create_string("Metacall", 8) + }; + + void *ret = metacallv_class(myclass, "hello", args, 1); + ASSERT_EQ((double)10, (double)metacall_value_to_double(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((double)10, (double)metacall_value_to_double(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_DOUBLE, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((double)124124, (double)metacall_value_to_double(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); + } + + //Value to napi class test + { + { + 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); + } + } +#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()); +}