From 67e344f96d820f00e70b325aa89783fd4cb1f3cf Mon Sep 17 00:00:00 2001 From: rxbryan Date: Sat, 28 Jan 2023 21:40:02 +0100 Subject: [PATCH] add base support for exporting objects and classes in node_loader --- .../node_loader/bootstrap/lib/bootstrap.js | 95 ++++- .../node_loader/source/node_loader_impl.cpp | 326 +++++++++--------- 2 files changed, 260 insertions(+), 161 deletions(-) diff --git a/source/loaders/node_loader/bootstrap/lib/bootstrap.js b/source/loaders/node_loader/bootstrap/lib/bootstrap.js index 2ad993732a..5c19d3e16b 100644 --- a/source/loaders/node_loader/bootstrap/lib/bootstrap.js +++ b/source/loaders/node_loader/bootstrap/lib/bootstrap.js @@ -266,6 +266,7 @@ function node_loader_trampoline_discover_function(func) { if (node_loader_trampoline_is_callable(func)) { // Espree can't parse native code functions so we can do a workaround const str = func.toString().replace('{ [native code] }', '{}'); + const ast = espree.parse(`(${str})`, { ecmaVersion: 14 }); @@ -276,7 +277,7 @@ function node_loader_trampoline_discover_function(func) { if (node_loader_trampoline_is_valid_symbol(node)) { const args = node_loader_trampoline_discover_arguments(node); const discover = { - ptr: func, + func, signature: args, async: node.async, }; @@ -293,6 +294,91 @@ function node_loader_trampoline_discover_function(func) { } } +function node_loader_trampoline_discover_klass_attributes(node) { + let attributes = []; + for (let i = 0; i < node.length; i++) { + if (node[i].kind === 'constructor') + { + for (let exp of node[i].value.body.body) + { + if (exp.type === 'ExpressionStatement' && exp.expression.type === 'AssignmentExpression') { + let left = exp.expression.left; + + if (left.type == 'MemberExpression' && (left.object && left.object.type === 'ThisExpression')) { + attributes.push(left.property && left.property.name); + } + } + } + } + } + + return attributes; +} + +function node_loader_trampoline_discover_klass_methods(node, str) { + const ret = {}; + for (let method of node) { + if (method.type === 'MethodDefinition') { + let method_name = method.key.name; + if (method.kind === 'constructor') { + method_name = 'klass_' + method_name; + } + ret[method_name] = { + name: method.key.name, + signature: node_loader_trampoline_discover_arguments(method.value) + } + + if (method.kind === 'method' && str.substring(method.start-1, method.start+5) === 'static') { + ret[method_name].static = true; + } + } + } + + return ret +} + +function node_loader_trampoline_discover_klass(klass) { + try { + if (node_loader_trampoline_is_callable(klass)) { + const str = klass.toString(); + const ast = espree.parse(`(${str})`, { + ecmaVersion: 14 + }); + + const node = (ast.body[0].type === 'ExpressionStatement') && ast.body[0].expression; + if (node.type === 'ClassExpression') { + const methods = node_loader_trampoline_discover_klass_methods(node.body.body, str) + const discover = { + klass, + methods + }; + + if (node.id && node.id.name) { + discover['name'] = node.id.name; + } + + if (methods.klass_constructor) { + discover['attributes'] = node_loader_trampoline_discover_klass_attributes(node.body.body); + } + + return discover; + } + } + } catch (ex) { + console.log(`Exception while parsing '${klass}' in node_loader_trampoline_discover_klass`, ex); + } +} + +function node_loader_trampoline_discover_object(obj) { + if (typeof obj === 'object') { + const constructor = (obj && obj.constructor) && obj.constructor.name + if (constructor !== 'Object' && constructor !== 'Array') + return { + obj + }; + } +} + function node_loader_trampoline_discover(handle) { const discover = {}; @@ -305,8 +391,9 @@ function node_loader_trampoline_discover(handle) { for (let j = 0; j < keys.length; ++j) { const key = keys[j]; - const func = exports[key]; - const descriptor = node_loader_trampoline_discover_function(func); + const value = exports[key]; + const descriptor = node_loader_trampoline_discover_function(value) + || node_loader_trampoline_discover_klass(value) || node_loader_trampoline_discover_object(value); if (descriptor !== undefined) { discover[key] = descriptor; @@ -411,6 +498,8 @@ module.exports = ((impl, ptr) => { 'clear': node_loader_trampoline_clear, 'discover': node_loader_trampoline_discover, 'discover_function': node_loader_trampoline_discover_function, + 'discover_klass': node_loader_trampoline_discover_klass, + 'discover_object': node_loader_trampoline_discover_object, 'test': node_loader_trampoline_test, 'await_function': node_loader_trampoline_await_function(trampoline), 'await_future': node_loader_trampoline_await_future(trampoline), diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index 76f14ccee1..4c66a9f147 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -3406,249 +3406,259 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf node_loader_impl_exception(env, status); - /* Get function pointer */ - status = napi_get_named_property(env, function_descriptor, "ptr", &function_ptr); + /* Check if function pointer exists */ + bool is_func = false; - node_loader_impl_exception(env, status); - - /* Check function pointer type */ - status = napi_typeof(env, function_ptr, &valuetype); + status = napi_has_named_property(env, function_descriptor, "func", &is_func); node_loader_impl_exception(env, status); - if (valuetype != napi_function) + if (is_func == true) { - napi_throw_type_error(env, nullptr, "Invalid NodeJS function"); - } + /* Get function pointer */ + status = napi_get_named_property(env, function_descriptor, "func", &function_ptr); - /* Get function signature */ - status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Check function pointer type */ + status = napi_typeof(env, function_ptr, &valuetype); - /* Check function pointer type */ - status = napi_typeof(env, function_sig, &valuetype); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS function"); + } - if (valuetype != napi_object) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); - } + /* Get function signature */ + status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); - /* Get signature length */ - status = napi_get_array_length(env, function_sig, &function_sig_length); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Check function pointer type */ + status = napi_typeof(env, function_sig, &valuetype); - /* Get function async */ - status = napi_get_named_property(env, function_descriptor, "async", &function_is_async); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); + } - /* Check function async type */ - status = napi_typeof(env, function_is_async, &valuetype); + /* Get signature length */ + status = napi_get_array_length(env, function_sig, &function_sig_length); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (valuetype != napi_boolean) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS async flag"); - } + /* Get function async */ + status = napi_get_named_property(env, function_descriptor, "async", &function_is_async); - /* Optionally retrieve types if any in order to support typed supersets of JavaScript like TypeScript */ - static const char types_str[] = "types"; - bool has_types = false; + node_loader_impl_exception(env, status); - status = napi_has_named_property(env, function_descriptor, types_str, &has_types); + /* Check function async type */ + status = napi_typeof(env, function_is_async, &valuetype); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (has_types == true) - { - status = napi_get_named_property(env, function_descriptor, types_str, &function_types); + if (valuetype != napi_boolean) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS async flag"); + } - node_loader_impl_exception(env, status); + /* Optionally retrieve types if any in order to support typed supersets of JavaScript like TypeScript */ + static const char types_str[] = "types"; + bool has_types = false; - /* Check types array type */ - status = napi_typeof(env, function_types, &valuetype); + status = napi_has_named_property(env, function_descriptor, types_str, &has_types); node_loader_impl_exception(env, status); - if (valuetype != napi_object) + if (has_types == true) { - napi_throw_type_error(env, nullptr, "Invalid NodeJS function types"); - } - } + status = napi_get_named_property(env, function_descriptor, types_str, &function_types); - /* Optionally retrieve return value type if any in order to support typed supersets of JavaScript like TypeScript */ - static const char ret_str[] = "ret"; - bool has_ret = false; + node_loader_impl_exception(env, status); - status = napi_has_named_property(env, function_descriptor, ret_str, &has_ret); + /* Check types array type */ + status = napi_typeof(env, function_types, &valuetype); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (has_ret == true) - { - status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS function types"); + } + } - node_loader_impl_exception(env, status); + /* Optionally retrieve return value type if any in order to support typed supersets of JavaScript like TypeScript */ + static const char ret_str[] = "ret"; + bool has_ret = false; - /* Check return value type */ - status = napi_typeof(env, function_ret, &valuetype); + status = napi_has_named_property(env, function_descriptor, ret_str, &has_ret); node_loader_impl_exception(env, status); - if (valuetype != napi_string) + if (has_ret == true) { - napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); - } - } + status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); - /* Create node function */ - loader_impl_node_function node_func = static_cast(malloc(sizeof(struct loader_impl_node_function_type))); - - /* Create reference to function pointer */ - status = napi_create_reference(env, function_ptr, 1, &node_func->func_ref); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Check return value type */ + status = napi_typeof(env, function_ret, &valuetype); - node_func->node_impl = discover_safe->node_impl; - node_func->impl = discover_safe->node_impl->impl; + node_loader_impl_exception(env, status); - /* Create function */ - function f = function_create(func_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); + if (valuetype != napi_string) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); + } + } - if (f != NULL) - { - signature s = function_signature(f); - scope sp = context_scope(discover_safe->ctx); - bool is_async = false; + /* Create node function */ + loader_impl_node_function node_func = static_cast(malloc(sizeof(struct loader_impl_node_function_type))); - /* Set function async */ - status = napi_get_value_bool(env, function_is_async, &is_async); + /* Create reference to function pointer */ + status = napi_create_reference(env, function_ptr, 1, &node_func->func_ref); node_loader_impl_exception(env, status); - function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); + node_func->node_impl = discover_safe->node_impl; + node_func->impl = discover_safe->node_impl->impl; - /* Set return value if any */ - if (has_ret) + /* Create function */ + function f = function_create(func_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); + + if (f != NULL) { - size_t return_type_length; - char *return_type_str = NULL; + signature s = function_signature(f); + scope sp = context_scope(discover_safe->ctx); + bool is_async = false; - /* Get return value string length */ - status = napi_get_value_string_utf8(env, function_ret, NULL, 0, &return_type_length); + /* Set function async */ + status = napi_get_value_bool(env, function_is_async, &is_async); node_loader_impl_exception(env, status); - if (return_type_length > 0) - { - return_type_str = static_cast(malloc(sizeof(char) * (return_type_length + 1))); - } + function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); - if (return_type_str != NULL) + /* Set return value if any */ + if (has_ret) { - /* Get parameter name string */ - status = napi_get_value_string_utf8(env, function_ret, return_type_str, return_type_length + 1, &return_type_length); - - node_loader_impl_exception(env, status); + size_t return_type_length; + char *return_type_str = NULL; - signature_set_return(s, loader_impl_type(discover_safe->node_impl->impl, return_type_str)); - - free(return_type_str); - } - } + /* Get return value string length */ + status = napi_get_value_string_utf8(env, function_ret, NULL, 0, &return_type_length); - /* Set signature */ - for (uint32_t arg_index = 0; arg_index < function_sig_length; ++arg_index) - { - napi_value parameter_name; - size_t parameter_name_length; - char *parameter_name_str = NULL; + node_loader_impl_exception(env, status); - /* Get signature parameter name */ - status = napi_get_element(env, function_sig, arg_index, ¶meter_name); + if (return_type_length > 0) + { + return_type_str = static_cast(malloc(sizeof(char) * (return_type_length + 1))); + } - node_loader_impl_exception(env, status); + if (return_type_str != NULL) + { + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, function_ret, return_type_str, return_type_length + 1, &return_type_length); - /* Get parameter name string length */ - status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + signature_set_return(s, loader_impl_type(discover_safe->node_impl->impl, return_type_str)); - if (parameter_name_length > 0) - { - parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); + free(return_type_str); + } } - /* Get parameter name string */ - status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); - - node_loader_impl_exception(env, status); - - /* Check if type info is available */ - if (has_types) + /* Set signature */ + for (uint32_t arg_index = 0; arg_index < function_sig_length; ++arg_index) { - napi_value parameter_type; - size_t parameter_type_length; - char *parameter_type_str = NULL; + napi_value parameter_name; + size_t parameter_name_length; + char *parameter_name_str = NULL; - /* Get signature parameter type */ - status = napi_get_element(env, function_types, arg_index, ¶meter_type); + /* Get signature parameter name */ + status = napi_get_element(env, function_sig, arg_index, ¶meter_name); node_loader_impl_exception(env, status); - /* Get parameter type string length */ - status = napi_get_value_string_utf8(env, parameter_type, NULL, 0, ¶meter_type_length); + /* Get parameter name string length */ + status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); node_loader_impl_exception(env, status); - if (parameter_type_length > 0) + if (parameter_name_length > 0) { - parameter_type_str = static_cast(malloc(sizeof(char) * (parameter_type_length + 1))); + parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); } - /* Get parameter type string */ - status = napi_get_value_string_utf8(env, parameter_type, parameter_type_str, parameter_type_length + 1, ¶meter_type_length); + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); node_loader_impl_exception(env, status); - signature_set(s, (size_t)arg_index, parameter_name_str, loader_impl_type(discover_safe->node_impl->impl, parameter_type_str)); + /* Check if type info is available */ + if (has_types) + { + napi_value parameter_type; + size_t parameter_type_length; + char *parameter_type_str = NULL; + + /* Get signature parameter type */ + status = napi_get_element(env, function_types, arg_index, ¶meter_type); + + node_loader_impl_exception(env, status); + + /* Get parameter type string length */ + status = napi_get_value_string_utf8(env, parameter_type, NULL, 0, ¶meter_type_length); + + node_loader_impl_exception(env, status); - if (parameter_type_str != NULL) + if (parameter_type_length > 0) + { + parameter_type_str = static_cast(malloc(sizeof(char) * (parameter_type_length + 1))); + } + + /* Get parameter type string */ + status = napi_get_value_string_utf8(env, parameter_type, parameter_type_str, parameter_type_length + 1, ¶meter_type_length); + + node_loader_impl_exception(env, status); + + signature_set(s, (size_t)arg_index, parameter_name_str, loader_impl_type(discover_safe->node_impl->impl, parameter_type_str)); + + if (parameter_type_str != NULL) + { + free(parameter_type_str); + } + } + else { - free(parameter_type_str); + signature_set(s, (size_t)arg_index, parameter_name_str, NULL); + } + + if (parameter_name_str != NULL) + { + free(parameter_name_str); } } - else - { - signature_set(s, (size_t)arg_index, parameter_name_str, NULL); - } - if (parameter_name_str != NULL) + value v = value_create_function(f); + + if (scope_define(sp, function_name(f), v) != 0) { - free(parameter_name_str); + value_type_destroy(v); + discover_safe->result = 1; + break; } } - - value v = value_create_function(f); - - if (scope_define(sp, function_name(f), v) != 0) + else { - value_type_destroy(v); + free(node_func); discover_safe->result = 1; break; } } - else - { - free(node_func); - discover_safe->result = 1; - break; - } free(func_name_str); } @@ -4007,7 +4017,7 @@ void *node_loader_impl_register(void *node_impl_ptr, void *env_ptr, void *functi #endif /* Store the amount of async handles that we have for the node loader, - * so we can count the user defined async handles */ + * so we can count the user defined async handles */ node_impl->base_active_handles = node_loader_impl_async_handles_count(node_impl); node_impl->extra_active_handles.store(0); node_impl->event_loop_empty.store(false); @@ -5251,10 +5261,10 @@ void node_loader_impl_destroy_safe_impl(loader_impl_node node_impl, napi_env env } /* NodeJS Loader needs to register that it is destroyed, because after this step - * some destructors can be still triggered, before the node_loader->destroy() has - * finished, so this destructors will try to execute the NodeJS unrefs while having - * the runtime (at least the NodeJS Loader related part) destroyed. - */ + * some destructors can be still triggered, before the node_loader->destroy() has + * finished, so this destructors will try to execute the NodeJS unrefs while having + * the runtime (at least the NodeJS Loader related part) destroyed. + */ loader_set_destroyed(node_impl->impl); }