]> git.kaiwu.me - njs.git/commitdiff
Parser: isolate optional preserve wrapper details.
authorDmitry Volyntsev <xeioex@nginx.com>
Wed, 4 Mar 2026 04:40:57 +0000 (20:40 -0800)
committerDmitry Volyntsev <xeioexception@gmail.com>
Thu, 12 Mar 2026 22:26:01 +0000 (15:26 -0700)
Introduce NJS_TOKEN_OPTIONAL_PRESERVE for optional-chain preserve nodes
instead of reusing OBJECT_VALUE, so OBJECT_VALUE remains strictly an
object/array literal structure token.

Route optional-preserve, object-value, and optional method-preserve
access through dedicated helper functions with narrow assertions,
removing direct u.object/left/right access from general parser and
generator paths.

No behavior change.

src/njs_generator.c
src/njs_lexer.h
src/njs_parser.c

index 12ccc527499718bd5dcbf9ce02e4050cfe3350da..6caea0d540aa3d10843ab9876491c84b08289db6 100644 (file)
@@ -124,9 +124,40 @@ njs_generate_is_property_call_source(njs_parser_node_t *node)
 }
 
 
+njs_inline njs_parser_node_t *
+njs_generate_wrapped_node(njs_parser_node_t *node)
+{
+    njs_assert(node->token_type == NJS_TOKEN_OBJECT_VALUE
+               || node->token_type == NJS_TOKEN_OPTIONAL_PRESERVE);
+
+    return node->u.object;
+}
+
+
+njs_inline njs_parser_node_t *
+njs_generate_object_value_parent(njs_parser_node_t *node)
+{
+    njs_assert(node->token_type == NJS_TOKEN_OBJECT_VALUE);
+
+    return njs_generate_wrapped_node(node);
+}
+
+
+njs_inline njs_bool_t
+njs_generate_is_object_literal_value(njs_parser_node_t *node)
+{
+    return node != NULL
+           && node->token_type == NJS_TOKEN_OBJECT_VALUE
+           && njs_generate_object_value_parent(node)->token_type
+              == NJS_TOKEN_OBJECT;
+}
+
+
 njs_inline njs_parser_node_t *
 njs_generate_optional_method_call_preserve(njs_parser_node_t *node)
 {
+    njs_assert(node->token_type == NJS_TOKEN_METHOD_CALL);
+
     return node->u.object;
 }
 
@@ -138,9 +169,33 @@ njs_generate_optional_method_call_property(njs_parser_node_t *node)
 }
 
 
+njs_inline njs_parser_node_t *
+njs_generate_optional_method_call_base(njs_parser_node_t *node)
+{
+    njs_parser_node_t  *preserve;
+
+    preserve = njs_generate_optional_method_call_preserve(node);
+
+    return (preserve != NULL) ? preserve->left : NULL;
+}
+
+
+njs_inline njs_parser_node_t *
+njs_generate_optional_method_call_key(njs_parser_node_t *node)
+{
+    njs_parser_node_t  *preserve;
+
+    preserve = njs_generate_optional_method_call_preserve(node);
+
+    return (preserve != NULL) ? preserve->right : NULL;
+}
+
+
 njs_inline njs_parser_node_t *
 njs_generate_optional_chain_preserve(njs_parser_node_t *node)
 {
+    njs_assert(node->token_type == NJS_TOKEN_OPTIONAL_CHAIN);
+
     return node->u.object;
 }
 
@@ -148,6 +203,8 @@ njs_generate_optional_chain_preserve(njs_parser_node_t *node)
 njs_inline njs_parser_node_t *
 njs_generate_function_call_this(njs_parser_node_t *node)
 {
+    njs_assert(node->token_type == NJS_TOKEN_FUNCTION_CALL);
+
     return node->u.object;
 }
 
@@ -787,7 +844,8 @@ njs_generate(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node)
         return njs_generator_stack_pop(vm, generator, NULL);
 
     case NJS_TOKEN_OBJECT_VALUE:
-        node->index = node->u.object->index;
+    case NJS_TOKEN_OPTIONAL_PRESERVE:
+        node->index = njs_generate_wrapped_node(node)->index;
         return njs_generator_stack_pop(vm, generator, NULL);
 
     case NJS_TOKEN_OBJECT:
@@ -3465,8 +3523,7 @@ njs_generate_assignment_end(njs_vm_t *vm, njs_generator_t *generator,
     case NJS_TOKEN_PROPERTY_INIT:
 
         if ((object->token_type == NJS_TOKEN_OBJECT
-             || (object->token_type == NJS_TOKEN_OBJECT_VALUE
-                 && object->u.object->token_type == NJS_TOKEN_OBJECT))
+             || njs_generate_is_object_literal_value(object))
             && (expr->token_type == NJS_TOKEN_FUNCTION
                 || expr->token_type == NJS_TOKEN_FUNCTION_EXPRESSION
                 || expr->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION))
@@ -4186,10 +4243,12 @@ njs_generate_optional_method_call(njs_vm_t *vm, njs_parser_node_t *node)
 {
     njs_parser_node_t  *preserve;
 
-    if (node == NULL
-        || node->token_type != NJS_TOKEN_METHOD_CALL
-        || node->u.object == NULL)
-    {
+    if (node == NULL || node->token_type != NJS_TOKEN_METHOD_CALL) {
+        return NULL;
+    }
+
+    preserve = njs_generate_optional_method_call_preserve(node);
+    if (preserve == NULL) {
         return NULL;
     }
 
@@ -4198,8 +4257,6 @@ njs_generate_optional_method_call(njs_vm_t *vm, njs_parser_node_t *node)
         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;
@@ -4225,7 +4282,7 @@ njs_generate_optional_chain(njs_vm_t *vm, njs_generator_t *generator,
 
     call = njs_generate_optional_method_call(vm, node->right);
     if (call != NULL) {
-        preserve = njs_generate_optional_method_call_preserve(call)->left;
+        preserve = njs_generate_optional_method_call_base(call);
 
         if (njs_generate_is_property_lvalue(preserve)) {
             preserve->hoist = 1;
@@ -4266,10 +4323,10 @@ njs_generate_optional_chain_after(njs_vm_t *vm, njs_generator_t *generator,
     call = njs_generate_optional_method_call(vm, node->right);
     if (call != NULL) {
         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;
+        prop->left->index = njs_generate_optional_method_call_base(call)
+                                ->index;
+        prop->right->index = njs_generate_optional_method_call_key(call)
+                                 ->index;
 
     } else if (njs_generate_optional_chain_preserve(node) != NULL) {
         njs_generate_optional_chain_preserve(node)->index = node->left->index;
@@ -4305,7 +4362,7 @@ njs_generate_optional_chain_end(njs_vm_t *vm, njs_generator_t *generator,
 
     call = njs_generate_optional_method_call(vm, node->right);
     if (call != NULL) {
-        preserve = njs_generate_optional_method_call_preserve(call)->left;
+        preserve = njs_generate_optional_method_call_base(call);
         if (!njs_generate_is_property_lvalue(preserve)) {
             preserve = NULL;
         }
index 5526be6c7cc0a50a07d14ffdde1a71da9bffa685..98b8dd87d70e64b9676a83681174f3a3a261b5ab 100644 (file)
@@ -131,6 +131,7 @@ typedef enum {
 
     NJS_TOKEN_OBJECT,
     NJS_TOKEN_OBJECT_VALUE,
+    NJS_TOKEN_OPTIONAL_PRESERVE,
     NJS_TOKEN_PROPERTY,
     NJS_TOKEN_PROPERTY_REF,
     NJS_TOKEN_PROPERTY_INIT,
index 5a56f0cdba73db2d9b8c20d4eb33b04f4eb7f313..9177ac2610969170bc8ff485d0434245fac8fe3a 100644 (file)
@@ -48,6 +48,8 @@ static njs_int_t njs_parser_object_literal(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_object_literal_after(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_parser_node_t *njs_parser_object_value(njs_parser_t *parser,
+    njs_parser_node_t *parent, uint32_t token_line);
 static njs_int_t njs_parser_property_definition_list(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_property_definition_list_after(
@@ -107,6 +109,10 @@ 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 void njs_parser_set_call_this(njs_parser_node_t *node,
+    njs_parser_node_t *this_object);
+static void njs_parser_set_optional_method_call_preserve(
+    njs_parser_node_t *node, njs_parser_node_t *preserve);
 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(
@@ -2700,7 +2706,7 @@ njs_parser_optional_chain_preserve(njs_parser_t *parser,
 {
     njs_parser_node_t  *ref;
 
-    ref = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE);
+    ref = njs_parser_node_new(parser, NJS_TOKEN_OPTIONAL_PRESERVE);
     if (ref == NULL) {
         return NULL;
     }
@@ -2712,10 +2718,21 @@ njs_parser_optional_chain_preserve(njs_parser_t *parser,
 }
 
 
+static void
+njs_parser_set_optional_chain_preserve(njs_parser_node_t *node,
+    njs_parser_node_t *preserve)
+{
+    njs_assert(node->token_type == NJS_TOKEN_OPTIONAL_CHAIN);
+    njs_assert(preserve->token_type == NJS_TOKEN_OPTIONAL_PRESERVE);
+
+    node->u.object = preserve;
+}
+
+
 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) {
+    if (node != NULL && node->token_type == NJS_TOKEN_OPTIONAL_PRESERVE) {
         return node->u.object;
     }
 
@@ -2769,7 +2786,7 @@ njs_parser_create_call(njs_parser_t *parser, njs_parser_node_t *node,
     }
 
     func->ctor = ctor;
-    func->u.object = this_object;
+    njs_parser_set_call_this(func, this_object);
 
     return func;
 }
@@ -2819,6 +2836,27 @@ njs_parser_optional_chain_receiver(njs_parser_node_t *node)
 }
 
 
+static njs_vmcode_t
+njs_parser_optional_chain_receiver_operation(njs_parser_node_t *node)
+{
+    return node->u.operation;
+}
+
+
+static njs_parser_node_t *
+njs_parser_optional_chain_receiver_base(njs_parser_node_t *node)
+{
+    return node->left;
+}
+
+
+static njs_parser_node_t *
+njs_parser_optional_chain_receiver_key(njs_parser_node_t *node)
+{
+    return node->right;
+}
+
+
 static njs_parser_node_t *
 njs_parser_optional_chain_call_this(njs_parser_node_t *node)
 {
@@ -2826,13 +2864,34 @@ njs_parser_optional_chain_call_this(njs_parser_node_t *node)
 
     receiver = njs_parser_optional_chain_receiver(node);
     if (receiver != NULL) {
-        return receiver->left;
+        return njs_parser_optional_chain_receiver_base(receiver);
     }
 
     return NULL;
 }
 
 
+static void
+njs_parser_set_call_this(njs_parser_node_t *node,
+    njs_parser_node_t *this_object)
+{
+    njs_assert(node->token_type == NJS_TOKEN_FUNCTION_CALL);
+
+    node->u.object = this_object;
+}
+
+
+static void
+njs_parser_set_optional_method_call_preserve(njs_parser_node_t *node,
+    njs_parser_node_t *preserve)
+{
+    njs_assert(node->token_type == NJS_TOKEN_METHOD_CALL);
+    njs_assert(njs_parser_call_receiver(preserve) != NULL);
+
+    node->u.object = preserve;
+}
+
+
 static njs_parser_node_t *
 njs_parser_optional_chain_target_property(njs_parser_node_t *node)
 {
@@ -2860,17 +2919,17 @@ njs_parser_optional_chain_method_call(njs_parser_t *parser,
         return NULL;
     }
 
-    prop->u.operation = src->u.operation;
+    prop->u.operation = njs_parser_optional_chain_receiver_operation(src);
     prop->token_line = token_line;
-    prop->left = src->left;
-    prop->right = src->right;
+    prop->left = njs_parser_optional_chain_receiver_base(src);
+    prop->right = njs_parser_optional_chain_receiver_key(src);
 
     func = njs_parser_create_call(parser, prop, 0);
     if (func == NULL) {
         return NULL;
     }
 
-    func->u.object = src;
+    njs_parser_set_optional_method_call_preserve(func, src);
 
     return func;
 }
@@ -3180,7 +3239,7 @@ njs_parser_optional_expression_after(njs_parser_t *parser,
         return NJS_ERROR;
     }
 
-    opt->u.object = ref;
+    njs_parser_set_optional_chain_preserve(opt, ref);
     parser->node = ref;
 
     njs_parser_next(parser, njs_parser_optional_chain);
@@ -8726,6 +8785,27 @@ njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token)
 }
 
 
+static njs_parser_node_t *
+njs_parser_object_value(njs_parser_t *parser, njs_parser_node_t *parent,
+    uint32_t token_line)
+{
+    njs_parser_node_t  *object;
+
+    njs_assert(parent->token_type == NJS_TOKEN_OBJECT
+               || parent->token_type == NJS_TOKEN_ARRAY);
+
+    object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE);
+    if (njs_slow_path(object == NULL)) {
+        return NULL;
+    }
+
+    object->token_line = token_line;
+    object->u.object = parent;
+
+    return object;
+}
+
+
 static njs_parser_node_t *
 njs_parser_argument(njs_parser_t *parser, njs_parser_node_t *expr,
     njs_index_t index)
@@ -8755,14 +8835,11 @@ njs_parser_object_property(njs_parser_t *parser, njs_parser_node_t *parent,
     njs_token_type_t   type;
     njs_parser_node_t  *stmt, *assign, *object, *propref;
 
-    object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE);
+    object = njs_parser_object_value(parser, parent, value->token_line);
     if (njs_slow_path(object == NULL)) {
         return NJS_TOKEN_ERROR;
     }
 
-    object->token_line = value->token_line;
-    object->u.object = parent;
-
     type = proto_init ? NJS_TOKEN_PROTO_INIT : NJS_TOKEN_PROPERTY_INIT;
 
     propref = njs_parser_node_new(parser, type);
@@ -8804,14 +8881,11 @@ njs_parser_property_accessor(njs_parser_t *parser, njs_parser_node_t *parent,
 {
     njs_parser_node_t  *node, *stmt, *object, *propref;
 
-    object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE);
+    object = njs_parser_object_value(parser, parent, value->token_line);
     if (njs_slow_path(object == NULL)) {
         return NJS_TOKEN_ERROR;
     }
 
-    object->token_line = value->token_line;
-    object->u.object = parent;
-
     propref = njs_parser_node_new(parser, 0);
     if (njs_slow_path(propref == NULL)) {
         return NJS_ERROR;
@@ -9778,6 +9852,7 @@ njs_parser_serialize_node(njs_chb_t *chain, njs_parser_node_t *node)
     njs_token_serialize(NJS_TOKEN_NAME);
     njs_token_serialize(NJS_TOKEN_OBJECT);
     njs_token_serialize(NJS_TOKEN_OBJECT_VALUE);
+    njs_token_serialize(NJS_TOKEN_OPTIONAL_PRESERVE);
     njs_token_serialize(NJS_TOKEN_ARRAY);
     njs_token_serialize(NJS_TOKEN_REGEXP);