django-vue3-admin-web/node_modules/aws-crt/source/class_binder.c
2025-10-20 21:21:14 +08:00

514 lines
16 KiB
C

/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include "class_binder.h"
#ifdef _MSC_VER
# pragma warning(disable : 4204)
#endif /* _MSC_VER */
struct aws_napi_class_info_impl {
const struct aws_napi_method_info *ctor_method;
napi_ref constructor;
struct {
void *instance;
napi_finalize finalizer;
bool is_wrapping;
} wrapping;
};
/* Make sure our static storage is big enough */
AWS_STATIC_ASSERT(sizeof(struct aws_napi_class_info) >= sizeof(struct aws_napi_class_info_impl));
/*
* Populates an aws_napi_argument object from a napi value.
*
* \param env The node environment.
* \param value The value to pull the value from.
* \param expected_type The type you expect the value to be. Pass napi_undefined to accept anything.
* \param accept_undefined Whether or not to accept expected_type OR undefined
* \param out_value The argument object to populate.
*/
static napi_status s_argument_parse(
napi_env env,
napi_value value,
napi_valuetype expected_type,
bool accept_undefined,
struct aws_napi_argument *out_value) {
out_value->node = value;
AWS_NAPI_CALL(env, napi_typeof(env, value, &out_value->type), { return status; });
if (expected_type != napi_undefined && out_value->type != expected_type &&
!(accept_undefined && out_value->type == napi_undefined)) {
switch (expected_type) {
case napi_string:
napi_throw_type_error(env, NULL, "Class binder argument expected a string");
return napi_string_expected;
case napi_number:
napi_throw_type_error(env, NULL, "Class binder argument expected a number");
return napi_number_expected;
default:
napi_throw_type_error(env, NULL, "Class binder argument wrong type");
return napi_generic_failure;
}
}
switch (out_value->type) {
case napi_boolean: {
AWS_NAPI_CALL(env, napi_get_value_bool(env, value, &out_value->native.boolean), { return status; });
break;
}
case napi_string: {
AWS_NAPI_CALL(env, aws_byte_buf_init_from_napi(&out_value->native.string, env, value), { return status; });
break;
}
case napi_number: {
AWS_NAPI_CALL(env, napi_get_value_int64(env, value, &out_value->native.number), {
napi_throw_type_error(env, NULL, "Class binder argument expected a number");
return status;
});
break;
}
case napi_external: {
AWS_NAPI_CALL(env, napi_get_value_external(env, value, &out_value->native.external), {
napi_throw_type_error(env, NULL, "Class binder argument expected an external");
return status;
});
break;
}
case napi_object: {
/* Attempt to unwrap the object, just in case */
napi_status result = napi_unwrap(env, value, &out_value->native.external);
if (result != napi_ok) {
out_value->native.external = NULL;
}
break;
}
default:
/* Don't process, just leave as node value */
break;
}
return napi_ok;
}
/*
* Cleans up an aws_napi_argument object populated by s_argument_parse.
*
* \param env The node environment.
* \param value The value to clean up.
*/
static void s_argument_cleanup(napi_env env, struct aws_napi_argument *value) {
(void)env;
switch (value->type) {
case napi_string:
aws_byte_buf_clean_up(&value->native.string);
break;
default:
break;
}
}
/*
* Used as the class's constructor
*/
static napi_value s_constructor(napi_env env, napi_callback_info info) {
napi_value node_args[AWS_NAPI_METHOD_MAX_ARGS];
napi_value node_this = NULL;
size_t num_args = AWS_ARRAY_SIZE(node_args);
struct aws_napi_class_info_impl *class_info = NULL;
AWS_NAPI_CALL(env, napi_get_cb_info(env, info, &num_args, node_args, &node_this, (void **)&class_info), {
napi_throw_error(env, NULL, "Failed to retreive callback information");
return NULL;
});
if (num_args > AWS_NAPI_METHOD_MAX_ARGS) {
num_args = AWS_NAPI_METHOD_MAX_ARGS;
}
napi_value result = NULL;
/* Check if we're wrapping an existing object or creating a new one */
if (class_info->wrapping.is_wrapping) {
AWS_FATAL_ASSERT(num_args == 0);
AWS_NAPI_CALL(
env,
napi_wrap(env, node_this, class_info->wrapping.instance, class_info->wrapping.finalizer, class_info, NULL),
{
napi_throw_error(env, NULL, "Failed to wrap http_request");
return NULL;
});
} else {
const struct aws_napi_method_info *method = class_info->ctor_method;
/* If there is no ctor method, don't both doing anything more, just return the empty object */
if (method->method) {
struct aws_napi_argument args[AWS_NAPI_METHOD_MAX_ARGS];
AWS_ZERO_ARRAY(args);
if (num_args < method->num_arguments) {
napi_throw_error(env, NULL, "Class binder constructor given incorrect number of arguments");
return NULL;
}
for (size_t i = 0; i < num_args; ++i) {
if (s_argument_parse(env, node_args[i], method->arg_types[i], i >= method->num_arguments, &args[i])) {
goto cleanup_arguments;
}
}
struct aws_napi_callback_info cb_info = {
.node_this = node_this,
.native_this = node_this,
.num_args = num_args,
};
cb_info.arguments = args;
method->method(env, &cb_info);
cleanup_arguments:
for (size_t i = 0; i < method->num_arguments; ++i) {
s_argument_cleanup(env, &args[i]);
}
}
}
return result;
}
/*
* Callback used to return the value of a property. Expects 0 arguments.
*/
static napi_value s_property_getter(napi_env env, napi_callback_info info) {
void *native_this = NULL;
napi_value node_this = NULL;
size_t num_args = 0;
void *data = NULL;
AWS_NAPI_CALL(env, napi_get_cb_info(env, info, &num_args, NULL, &node_this, &data), {
napi_throw_error(env, NULL, "Failed to retreive callback information");
return NULL;
});
if (num_args != 0) {
napi_throw_error(env, NULL, "Class binder getter needs exactly 0 arguments");
return NULL;
}
AWS_NAPI_CALL(env, napi_unwrap(env, node_this, &native_this), {
napi_throw_error(env, NULL, "Class binder property getter must be called on a wrapped object");
return NULL;
});
const struct aws_napi_property_info *property = data;
if (!property->getter) {
napi_throw_error(env, NULL, "Property is not readable");
return NULL;
}
napi_value result = property->getter(env, native_this);
#if DEBUG_BUILD
/* In debug builds, validate that getters are returning the correct type */
napi_valuetype result_type = napi_undefined;
AWS_NAPI_CALL(env, napi_typeof(env, result, &result_type), { return NULL; });
AWS_FATAL_ASSERT(property->type == napi_undefined || property->type == result_type);
#endif
return result;
}
/*
* Callback used to set the value of a property. Expects 1 argument.
*/
static napi_value s_property_setter(napi_env env, napi_callback_info info) {
void *native_this = NULL;
napi_value node_this = NULL;
napi_value node_value;
size_t num_args = 1;
void *data = NULL;
AWS_NAPI_CALL(env, napi_get_cb_info(env, info, &num_args, &node_value, &node_this, &data), {
napi_throw_error(env, NULL, "Failed to retreive callback information");
return NULL;
});
if (num_args != 1) {
napi_throw_error(env, NULL, "Class binder setter needs exactly 1 arguments");
return NULL;
}
AWS_NAPI_CALL(env, napi_unwrap(env, node_this, &native_this), {
napi_throw_error(env, NULL, "Class binder setter must be called on instance of a wrapped object");
return NULL;
});
const struct aws_napi_property_info *property = data;
if (!property->setter) {
napi_throw_error(env, NULL, "Property is not writable");
return NULL;
}
struct aws_napi_argument new_value;
if (s_argument_parse(env, node_value, property->type, false, &new_value)) {
return NULL;
}
property->setter(env, native_this, &new_value);
s_argument_cleanup(env, &new_value);
return NULL;
}
/*
* Callback used to call a method on a bound object.
*/
static napi_value s_method_call(napi_env env, napi_callback_info info) {
void *native_this = NULL;
struct aws_napi_argument args[AWS_NAPI_METHOD_MAX_ARGS];
AWS_ZERO_ARRAY(args);
napi_value node_this = NULL;
napi_value node_args[AWS_NAPI_METHOD_MAX_ARGS];
size_t num_args = AWS_ARRAY_SIZE(node_args);
void *data = NULL;
AWS_NAPI_CALL(env, napi_get_cb_info(env, info, &num_args, node_args, &node_this, &data), {
napi_throw_error(env, NULL, "Failed to retreive callback information");
return NULL;
});
if (num_args > AWS_NAPI_METHOD_MAX_ARGS) {
num_args = AWS_NAPI_METHOD_MAX_ARGS;
}
struct aws_napi_method_info *method = data;
if (num_args < method->num_arguments) {
napi_throw_error(env, NULL, "Bound class's method requires more arguments");
return NULL;
}
if ((method->attributes & napi_static) == 0) {
AWS_NAPI_CALL(env, napi_unwrap(env, node_this, &native_this), {
napi_throw_error(env, NULL, "Bound class's method must be called on instance of the class");
return NULL;
});
}
napi_value result = NULL;
for (size_t i = 0; i < num_args; ++i) {
if (s_argument_parse(env, node_args[i], method->arg_types[i], i >= method->num_arguments, &args[i])) {
goto cleanup_arguments;
}
}
struct aws_napi_callback_info cb_info = {
.node_this = node_this,
.native_this = native_this,
.num_args = num_args,
};
cb_info.arguments = args;
result = method->method(env, &cb_info);
cleanup_arguments:
for (size_t i = 0; i < num_args; ++i) {
s_argument_cleanup(env, &args[i]);
}
return result;
}
bool aws_napi_method_next_argument(
napi_valuetype expected_type,
const struct aws_napi_callback_info *cb_info,
const struct aws_napi_argument **next_arg) {
if (!*next_arg) {
*next_arg = cb_info->arguments;
} else {
(*next_arg)++;
}
const size_t current_index = (*next_arg) - cb_info->arguments;
return current_index <= cb_info->num_args &&
((expected_type == napi_undefined && (*next_arg)->type != napi_undefined) ||
(expected_type == (*next_arg)->type));
}
static napi_status s_get_symbol(napi_env env, const char *symbol_name, napi_value *result) {
napi_value global = NULL;
AWS_NAPI_CALL(env, napi_get_global(env, &global), { return status; });
napi_value symbol = NULL;
AWS_NAPI_CALL(env, napi_get_named_property(env, global, "Symbol", &symbol), { return status; });
AWS_NAPI_CALL(env, napi_get_named_property(env, symbol, symbol_name, result), { return status; });
return napi_ok;
}
napi_status aws_napi_define_class(
napi_env env,
napi_value exports,
const struct aws_napi_method_info *constructor,
const struct aws_napi_property_info *properties,
size_t num_properties,
const struct aws_napi_method_info *methods,
size_t num_methods,
struct aws_napi_class_info *class_info) {
AWS_FATAL_ASSERT(constructor->name);
AWS_FATAL_ASSERT(constructor->attributes == napi_default);
struct aws_napi_class_info_impl *impl = (struct aws_napi_class_info_impl *)class_info;
impl->ctor_method = constructor;
struct aws_allocator *allocator = aws_napi_get_allocator();
const size_t num_descriptors = num_properties + num_methods;
napi_property_descriptor *descriptors =
aws_mem_calloc(allocator, num_descriptors, sizeof(napi_property_descriptor));
size_t desc_i = 0;
for (size_t prop_i = 0; prop_i < num_properties; ++prop_i) {
napi_property_descriptor *desc = &descriptors[desc_i++];
const struct aws_napi_property_info *property = &properties[prop_i];
AWS_FATAL_ASSERT(property->name || property->symbol);
AWS_FATAL_ASSERT(property->getter || property->setter);
if (property->symbol) {
AWS_NAPI_CALL(env, s_get_symbol(env, property->symbol, &desc->name), { return status; });
} else {
desc->utf8name = property->name;
}
desc->data = (void *)property;
desc->getter = s_property_getter;
desc->setter = s_property_setter;
desc->attributes = property->attributes;
}
for (size_t method_i = 0; method_i < num_methods; ++method_i) {
napi_property_descriptor *desc = &descriptors[desc_i++];
const struct aws_napi_method_info *method = &methods[method_i];
AWS_FATAL_ASSERT(method->name || method->symbol);
AWS_FATAL_ASSERT(method->method);
if (method->symbol) {
AWS_NAPI_CALL(env, s_get_symbol(env, method->symbol, &desc->name), { return status; });
} else {
desc->utf8name = method->name;
}
desc->data = (void *)method;
desc->method = s_method_call;
desc->attributes = method->attributes;
}
napi_value node_constructor = NULL;
AWS_NAPI_CALL(
env,
napi_define_class(
env,
constructor->name,
NAPI_AUTO_LENGTH,
s_constructor,
class_info,
num_descriptors,
descriptors,
&node_constructor),
{ return status; });
/* Don't need descriptors anymore */
aws_mem_release(allocator, descriptors);
/* Reference the constructor for later user */
AWS_NAPI_CALL(env, napi_create_reference(env, node_constructor, 1, &impl->constructor), { return status; });
AWS_NAPI_CALL(env, napi_set_named_property(env, exports, constructor->name, node_constructor), { return status; });
return napi_ok;
}
napi_status aws_napi_wrap(
napi_env env,
struct aws_napi_class_info *class_info,
void *native,
napi_finalize finalizer,
napi_value *result) {
struct aws_napi_class_info_impl *impl = (struct aws_napi_class_info_impl *)class_info;
/* Retrieve the constructor */
napi_value constructor = NULL;
AWS_NAPI_CALL(env, napi_get_reference_value(env, impl->constructor, &constructor), {
napi_throw_error(env, NULL, "Failed to dereference constructor value");
return status;
});
/* Set our handy-dandy global state */
impl->wrapping.is_wrapping = true;
impl->wrapping.instance = native;
impl->wrapping.finalizer = finalizer;
AWS_NAPI_CALL(env, napi_new_instance(env, constructor, 0, NULL, result), {
napi_throw_error(env, NULL, "Failed to construct class-bound object");
return status;
});
AWS_ZERO_STRUCT(impl->wrapping);
/* The constructor function will have called napi_wrap onto result */
return napi_ok;
}
napi_status aws_napi_define_function(napi_env env, napi_value exports, struct aws_napi_method_info *method) {
/* Set static attribute so that s_method_call doesn't try to unwrap the this object */
method->attributes = napi_static;
/* Create the function object */
napi_value node_function = NULL;
AWS_NAPI_CALL(
env, napi_create_function(env, method->name, NAPI_AUTO_LENGTH, s_method_call, method, &node_function), {
return status;
});
/* Initialize the name from the symbol if necessary, otherwise use the utf8 name */
napi_value node_name = NULL;
if (method->symbol) {
AWS_NAPI_CALL(env, s_get_symbol(env, method->symbol, &node_name), { return status; });
} else {
AWS_NAPI_CALL(
env, napi_create_string_utf8(env, method->name, NAPI_AUTO_LENGTH, &node_name), { return status; });
}
/* Export the function */
AWS_NAPI_CALL(env, napi_set_property(env, exports, node_name, node_function), { return status; });
return napi_ok;
}