}
+njs_inline njs_bool_t
+njs_generate_is_property_call_source(njs_parser_node_t *node)
+{
+ return node != NULL && node->token_type == NJS_TOKEN_PROPERTY_REF;
+}
+
+
+njs_inline njs_parser_node_t *
+njs_generate_optional_method_call_preserve(njs_parser_node_t *node)
+{
+ return node->u.object;
+}
+
+
+njs_inline njs_parser_node_t *
+njs_generate_optional_method_call_property(njs_parser_node_t *node)
+{
+ return node->left;
+}
+
+
+njs_inline njs_parser_node_t *
+njs_generate_optional_chain_preserve(njs_parser_node_t *node)
+{
+ return node->u.object;
+}
+
+
+njs_inline njs_parser_node_t *
+njs_generate_function_call_this(njs_parser_node_t *node)
+{
+ return node->u.object;
+}
+
+
static u_char *njs_generate_reserve(njs_vm_t *vm, njs_generator_t *generator,
size_t size);
static njs_int_t njs_generate_code_map(njs_vm_t *vm, njs_generator_t *generator,
static njs_parser_node_t *
-njs_generate_optional_method_call(njs_parser_node_t *node)
+njs_generate_optional_method_call(njs_vm_t *vm, njs_parser_node_t *node)
{
- if (node != NULL
- && node->token_type == NJS_TOKEN_METHOD_CALL
- && node->u.object != NULL)
+ njs_parser_node_t *preserve;
+
+ if (node == NULL
+ || node->token_type != NJS_TOKEN_METHOD_CALL
+ || node->u.object == NULL)
{
- return node;
+ return NULL;
}
- return NULL;
+ if (!njs_generate_is_property_call_source(node->left)) {
+ njs_internal_error(vm, "unexpected optional method call source");
+ return NULL;
+ }
+
+ preserve = njs_generate_optional_method_call_preserve(node);
+
+ if (preserve->left == NULL || preserve->right == NULL) {
+ njs_internal_error(vm, "unexpected optional method call state");
+ return NULL;
+ }
+
+ if (!njs_generate_is_property_lvalue(preserve)) {
+ njs_internal_error(vm, "unexpected optional method call preserve");
+ return NULL;
+ }
+
+ return node;
}
jump_offset = 0;
- call = njs_generate_optional_method_call(node->right);
+ call = njs_generate_optional_method_call(vm, node->right);
if (call != NULL) {
- preserve = call->u.object->left;
+ preserve = njs_generate_optional_method_call_preserve(call)->left;
if (njs_generate_is_property_lvalue(preserve)) {
preserve->hoist = 1;
test_jump->retval = node->index;
- call = njs_generate_optional_method_call(node->right);
+ call = njs_generate_optional_method_call(vm, node->right);
if (call != NULL) {
- prop = call->left;
- prop->left->index = call->u.object->left->index;
- prop->right->index = call->u.object->right->index;
+ prop = njs_generate_optional_method_call_property(call);
+ prop->left->index = njs_generate_optional_method_call_preserve(call)
+ ->left->index;
+ prop->right->index = njs_generate_optional_method_call_preserve(call)
+ ->right->index;
- } else if (node->u.object != NULL) {
- node->u.object->index = node->left->index;
+ } else if (njs_generate_optional_chain_preserve(node) != NULL) {
+ njs_generate_optional_chain_preserve(node)->index = node->left->index;
}
njs_generator_next(generator, njs_generate, node->right);
njs_code_set_jump_offset(generator, njs_vmcode_test_jump_t,
*jump_offset);
- call = njs_generate_optional_method_call(node->right);
+ call = njs_generate_optional_method_call(vm, node->right);
if (call != NULL) {
- preserve = call->u.object->left;
-
+ preserve = njs_generate_optional_method_call_preserve(call)->left;
if (!njs_generate_is_property_lvalue(preserve)) {
preserve = NULL;
}
njs_generate_function_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_variable_t *var;
+ njs_int_t ret;
+ njs_variable_t *var;
+ njs_parser_node_t *this_object;
var = NULL;
+ this_object = njs_generate_function_call_this(node);
if (node->left != NULL) {
+ if (njs_generate_is_property_call_source(node->left)) {
+ njs_internal_error(vm, "unexpected function call source");
+ return NJS_ERROR;
+ }
+
+ if (this_object != NULL
+ && node->left->token_type != NJS_TOKEN_OPTIONAL_CHAIN)
+ {
+ njs_internal_error(vm, "unexpected function call this");
+ return NJS_ERROR;
+ }
+
/* Generate function code in function expression. */
njs_generator_next(generator, njs_generate, node->left);
njs_generate_function_call_end(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
+ njs_int_t ret;
+ njs_parser_node_t *this_object;
ret = njs_generate_call(vm, generator, node);
if (njs_fast_path(ret != NJS_OK)) {
return ret;
}
+ this_object = njs_generate_function_call_this(node);
+ if (this_object != NULL && this_object->temporary) {
+ ret = njs_generate_index_release(vm, generator, this_object->index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
return njs_generator_stack_pop(vm, generator, generator->context);
}
njs_int_t ret;
njs_parser_node_t *prop;
- if (njs_generate_optional_method_call(node) != NULL) {
+ if (njs_generate_optional_method_call(vm, node) != NULL) {
return njs_generate_method_call_arguments(vm, generator, node);
}
prop = node->left;
+ if (!njs_generate_is_property_call_source(prop)) {
+ njs_internal_error(vm, "unexpected method call source");
+ return NJS_ERROR;
+ }
+
/* Object. */
njs_generator_next(generator, njs_generate, prop->left);
prop = node->left;
+ if (!njs_generate_is_property_call_source(prop)) {
+ njs_internal_error(vm, "unexpected method call source");
+ return NJS_ERROR;
+ }
+
njs_generate_code(generator, njs_vmcode_method_frame_t, method,
NJS_VMCODE_METHOD_FRAME, prop);
method_offset = njs_code_offset(generator, method);
static njs_int_t njs_parser_right_link_pop(njs_parser_t *parser);
static njs_parser_node_t *njs_parser_property_ref(njs_parser_node_t *node);
static njs_parser_node_t *njs_parser_lvalue_ref(njs_parser_node_t *node);
+static njs_parser_node_t *njs_parser_call_receiver(njs_parser_node_t *node);
+static njs_parser_node_t *njs_parser_optional_chain_preserve(
+ njs_parser_t *parser, njs_parser_node_t *node, uint32_t token_line);
+static njs_parser_node_t *njs_parser_optional_chain_source(
+ njs_parser_node_t *node);
+static njs_parser_node_t *njs_parser_optional_chain_receiver(
+ njs_parser_node_t *node);
+static njs_parser_node_t *njs_parser_optional_chain_call_this(
+ njs_parser_node_t *node);
+static njs_parser_node_t *njs_parser_optional_chain_target_property(
+ njs_parser_node_t *node);
static njs_parser_node_t *njs_parser_optional_chain_method_call(
njs_parser_t *parser, njs_parser_node_t *node, uint32_t token_line);
+static njs_parser_node_t *njs_parser_optional_chain_call(
+ njs_parser_t *parser, njs_parser_node_t *node,
+ njs_parser_node_t *fallback, uint32_t token_line);
static njs_int_t njs_parser_call_expression(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_call_expression_args(njs_parser_t *parser,
static njs_parser_node_t *
-njs_parser_create_call(njs_parser_t *parser, njs_parser_node_t *node,
- uint8_t ctor)
+njs_parser_call_receiver(njs_parser_node_t *node)
{
- njs_parser_node_t *func;
+ return njs_parser_property_ref(node);
+}
- switch (node->token_type) {
- case NJS_TOKEN_NAME:
- func = node;
- func->token_type = NJS_TOKEN_FUNCTION_CALL;
- break;
+static njs_parser_node_t *
+njs_parser_optional_chain_preserve(njs_parser_t *parser,
+ njs_parser_node_t *node, uint32_t token_line)
+{
+ njs_parser_node_t *ref;
- case NJS_TOKEN_PROPERTY:
- case NJS_TOKEN_PROPERTY_REF:
- node = njs_parser_property_ref(node);
- if (node == NULL) {
- return NULL;
- }
+ ref = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE);
+ if (ref == NULL) {
+ return NULL;
+ }
+
+ ref->token_line = token_line;
+ ref->u.object = node;
+
+ return ref;
+}
+
+
+static njs_parser_node_t *
+njs_parser_optional_chain_source(njs_parser_node_t *node)
+{
+ if (node != NULL && node->token_type == NJS_TOKEN_OBJECT_VALUE) {
+ return node->u.object;
+ }
+ return NULL;
+}
+
+
+static njs_parser_node_t *
+njs_parser_create_call(njs_parser_t *parser, njs_parser_node_t *node,
+ uint8_t ctor)
+{
+ njs_parser_node_t *func, *receiver, *this_object;
+
+ receiver = njs_parser_call_receiver(node);
+ if (receiver != NULL) {
func = njs_parser_node_new(parser, NJS_TOKEN_METHOD_CALL);
if (func == NULL) {
return NULL;
}
- func->left = node;
+ func->left = receiver;
+ func->ctor = ctor;
+
+ return func;
+ }
+
+ this_object = njs_parser_optional_chain_call_this(node);
+
+ switch (node->token_type) {
+ case NJS_TOKEN_NAME:
+ func = node;
+ func->token_type = NJS_TOKEN_FUNCTION_CALL;
+
break;
default:
}
func->ctor = ctor;
+ func->u.object = this_object;
return func;
}
static njs_parser_node_t *
-njs_parser_optional_chain_property(njs_parser_node_t *node)
+njs_parser_optional_chain_receiver(njs_parser_node_t *node)
{
- if (node == NULL) {
- return NULL;
- }
-
- if (node->token_type == NJS_TOKEN_PROPERTY
- || node->token_type == NJS_TOKEN_PROPERTY_REF)
- {
+ if (njs_parser_call_receiver(node) != NULL) {
return node;
}
- if (node->token_type == NJS_TOKEN_OPTIONAL_CHAIN
+ if (node != NULL
+ && node->token_type == NJS_TOKEN_OPTIONAL_CHAIN
&& node->right != NULL
- && (node->right->token_type == NJS_TOKEN_PROPERTY
- || node->right->token_type == NJS_TOKEN_PROPERTY_REF))
+ && njs_parser_call_receiver(node->right) != NULL)
{
return node->right;
}
}
+static njs_parser_node_t *
+njs_parser_optional_chain_call_this(njs_parser_node_t *node)
+{
+ njs_parser_node_t *receiver;
+
+ receiver = njs_parser_optional_chain_receiver(node);
+ if (receiver != NULL) {
+ return receiver->left;
+ }
+
+ return NULL;
+}
+
+
+static njs_parser_node_t *
+njs_parser_optional_chain_target_property(njs_parser_node_t *node)
+{
+ if (node == NULL || node->token_type != NJS_TOKEN_OPTIONAL_CHAIN) {
+ return NULL;
+ }
+
+ return njs_parser_property_ref(node->right);
+}
+
+
static njs_parser_node_t *
njs_parser_optional_chain_method_call(njs_parser_t *parser,
njs_parser_node_t *node, uint32_t token_line)
{
njs_parser_node_t *func, *prop, *src;
- src = njs_parser_optional_chain_property(node);
+ src = njs_parser_optional_chain_receiver(node);
if (src == NULL) {
return NULL;
}
prop->left = src->left;
prop->right = src->right;
- func = njs_parser_node_new(parser, NJS_TOKEN_METHOD_CALL);
+ func = njs_parser_create_call(parser, prop, 0);
if (func == NULL) {
return NULL;
}
- func->left = prop;
func->u.object = src;
return func;
}
+static njs_parser_node_t *
+njs_parser_optional_chain_call(njs_parser_t *parser, njs_parser_node_t *node,
+ njs_parser_node_t *fallback, uint32_t token_line)
+{
+ njs_parser_node_t *func;
+
+ func = njs_parser_optional_chain_method_call(parser, node, token_line);
+ if (func != NULL) {
+ return func;
+ }
+
+ return njs_parser_create_call(parser, fallback, 0);
+}
+
+
static njs_int_t
njs_parser_call_expression(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
opt->left = parser->node;
opt->left->dest = opt;
- ref = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE);
+ ref = njs_parser_optional_chain_preserve(parser, parser->node,
+ token->line);
if (ref == NULL) {
return NJS_ERROR;
}
- ref->token_line = token->line;
- ref->u.object = parser->node;
opt->u.object = ref;
parser->node = ref;
switch (token->type) {
case NJS_TOKEN_OPEN_PARENTHESIS:
- func = njs_parser_optional_chain_method_call(parser,
- parser->node->u.object,
- token->line);
+ func = njs_parser_optional_chain_call(parser,
+ njs_parser_optional_chain_source(
+ parser->node),
+ parser->node, token->line);
if (func == NULL) {
- func = njs_parser_create_call(parser, parser->node, 0);
- if (func == NULL) {
- return NJS_ERROR;
- }
+ return NJS_ERROR;
}
ret = njs_parser_call_arguments(parser, token, current, func,
return njs_parser_stack_pop(parser);
case NJS_TOKEN_OPTIONAL_CHAIN:
- prop = njs_parser_optional_chain_property(node);
+ prop = njs_parser_optional_chain_target_property(node);
if (prop != NULL) {
prop->token_type = NJS_TOKEN_PROPERTY_DELETE;
prop->u.operation = NJS_VMCODE_PROPERTY_DELETE;