} njs_generator_try_ctx_t;
+typedef struct {
+ njs_jump_off_t jump_offset;
+ njs_index_t prop_index;
+} njs_generator_log_assign_ctx_t;
+
+
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,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_operation_assignment_end(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_logical_assignment(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_logical_assignment_prop(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_logical_assignment_end(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_object(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
static njs_int_t njs_generate_property_accessor(njs_vm_t *vm,
case NJS_TOKEN_REMAINDER_ASSIGNMENT:
return njs_generate_operation_assignment(vm, generator, node);
+ case NJS_TOKEN_LOGICAL_OR_ASSIGNMENT:
+ case NJS_TOKEN_LOGICAL_AND_ASSIGNMENT:
+ return njs_generate_logical_assignment(vm, generator, node);
+
case NJS_TOKEN_BITWISE_OR:
case NJS_TOKEN_BITWISE_XOR:
case NJS_TOKEN_BITWISE_AND:
}
+static njs_int_t
+njs_generate_logical_assignment(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_variable_t *var;
+ njs_parser_node_t *lvalue;
+ njs_vmcode_variable_t *var_code;
+ njs_generator_log_assign_ctx_t ctx;
+
+ lvalue = node->left;
+
+ if (lvalue->token_type == NJS_TOKEN_NAME) {
+
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_REFERENCE,
+ &var);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_generate_test_jump(vm, generator, node, node->u.operation,
+ lvalue->index, lvalue->index,
+ &ctx.jump_offset);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (var != NULL && var->type == NJS_VARIABLE_CONST) {
+ njs_generate_code(generator, njs_vmcode_variable_t, var_code,
+ NJS_VMCODE_ASSIGNMENT_ERROR, node);
+ var_code->dst = var->index;
+
+ njs_code_set_jump_offset(generator, njs_vmcode_test_jump_t,
+ ctx.jump_offset);
+
+ node->index = lvalue->index;
+
+ return njs_generator_stack_pop(vm, generator, NULL);
+ }
+
+ ctx.prop_index = NJS_INDEX_NONE;
+
+ njs_generator_next(generator, njs_generate, node->right);
+
+ return njs_generator_after(vm, generator,
+ njs_queue_first(&generator->stack), node,
+ njs_generate_logical_assignment_end,
+ &ctx,
+ sizeof(njs_generator_log_assign_ctx_t));
+ }
+
+ /* lvalue->token == NJS_TOKEN_PROPERTY */
+
+ /* Object. */
+
+ njs_generator_next(generator, njs_generate, lvalue->left);
+
+ ret = njs_generator_after(vm, generator,
+ njs_queue_first(&generator->stack), node,
+ njs_generate_logical_assignment_prop, NULL, 0);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ /* Property. */
+
+ return njs_generator_after(vm, generator,
+ njs_queue_first(&generator->stack),
+ lvalue->right, njs_generate, NULL, 0);
+}
+
+
+static njs_int_t
+njs_generate_logical_assignment_prop(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_generator_log_assign_ctx_t ctx;
+
+ ret = njs_generate_read_property_assignment(vm, generator, node,
+ node->right, &ctx.prop_index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_generate_test_jump(vm, generator, node, node->u.operation,
+ node->index, node->index,
+ &ctx.jump_offset);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ njs_generator_next(generator, njs_generate, node->right);
+
+ return njs_generator_after(vm, generator,
+ njs_queue_first(&generator->stack), node,
+ njs_generate_logical_assignment_end,
+ &ctx,
+ sizeof(njs_generator_log_assign_ctx_t));
+}
+
+
+static njs_int_t
+njs_generate_logical_assignment_end(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_index_t index;
+ njs_parser_node_t *lvalue, *expr;
+ njs_vmcode_move_t *move;
+ njs_generator_log_assign_ctx_t *ctx;
+
+ lvalue = node->left;
+ expr = node->right;
+
+ ctx = generator->context;
+
+ index = (lvalue->token_type == NJS_TOKEN_NAME) ? lvalue->index
+ : node->index;
+
+ if (index != expr->index) {
+ njs_generate_code_move(generator, move, index,
+ expr->index, node);
+ }
+
+ njs_code_set_jump_offset(generator, njs_vmcode_test_jump_t,
+ ctx->jump_offset);
+
+ if (ctx->prop_index == NJS_INDEX_NONE) {
+ ret = njs_generate_global_property_set(vm, generator, lvalue, expr);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ node->index = lvalue->index;
+
+ ret = njs_generate_node_index_release(vm, generator, expr);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ return njs_generator_stack_pop(vm, generator, generator->context);
+ }
+
+ ret = njs_generate_property_set(vm, generator, lvalue->right, node->index,
+ lvalue->left->index, ctx->prop_index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_generate_children_indexes_release(vm, generator, lvalue);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ return njs_generate_node_index_release_pop(vm, generator, expr);
+}
+
+
static njs_int_t
njs_generate_object(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{ njs_str("null ?? 0 || 1"),
njs_str("SyntaxError: Unexpected token \"||\"") },
+ /* Logical assignment: ||= */
+
+ { njs_str("var a = 0; a ||= 5; a"),
+ njs_str("5") },
+ { njs_str("var a = 1; a ||= 5; a"),
+ njs_str("1") },
+ { njs_str("var a = ''; a ||= 'x'; a"),
+ njs_str("x") },
+ { njs_str("var a = 'y'; a ||= 'x'; a"),
+ njs_str("y") },
+ { njs_str("var a = null; a ||= 42; a"),
+ njs_str("42") },
+ { njs_str("var a = undefined; a ||= 1"),
+ njs_str("1") },
+
+ /* ||= short-circuit: RHS not evaluated */
+
+ { njs_str("var a = 1; var b = 0; a ||= (b = 2); b"),
+ njs_str("0") },
+ { njs_str("var a = 0; var b = 0; a ||= (b = 2); b"),
+ njs_str("2") },
+
+ /* Logical assignment: &&= */
+
+ { njs_str("var a = 1; a &&= 5; a"),
+ njs_str("5") },
+ { njs_str("var a = 0; a &&= 5; a"),
+ njs_str("0") },
+ { njs_str("var a = 'y'; a &&= 'x'; a"),
+ njs_str("x") },
+ { njs_str("var a = ''; a &&= 'x'; a"),
+ njs_str("") },
+
+ /* &&= short-circuit: RHS not evaluated */
+
+ { njs_str("var a = 0; var b = 0; a &&= (b = 2); b"),
+ njs_str("0") },
+ { njs_str("var a = 1; var b = 0; a &&= (b = 2); b"),
+ njs_str("2") },
+
+ /* Logical assignment: property targets */
+
+ { njs_str("var o = {a: 0}; o.a ||= 5; o.a"),
+ njs_str("5") },
+ { njs_str("var o = {a: 1}; o.a ||= 5; o.a"),
+ njs_str("1") },
+ { njs_str("var o = {a: 1}; o.a &&= 5; o.a"),
+ njs_str("5") },
+ { njs_str("var o = {a: 0}; o.a &&= 5; o.a"),
+ njs_str("0") },
+
+ /* Logical assignment: bracket property targets */
+
+ { njs_str("var o = {a: 0}; o['a'] ||= 5; o.a"),
+ njs_str("5") },
+ { njs_str("var o = {a: 1}; o['a'] &&= 5; o.a"),
+ njs_str("5") },
+
+ /* Logical assignment: expression result value */
+
+ { njs_str("var a = 1; (a ||= 5)"),
+ njs_str("1") },
+ { njs_str("var a = 0; (a ||= 5)"),
+ njs_str("5") },
+ { njs_str("var a = 1; (a &&= 5)"),
+ njs_str("5") },
+ { njs_str("var a = 0; (a &&= 5)"),
+ njs_str("0") },
+
+ /* Logical assignment: const error */
+
+ { njs_str("const a = 1; a ||= 2"),
+ njs_str("1") },
+ { njs_str("const a = 0; a ||= 2"),
+ njs_str("TypeError: assignment to constant variable") },
+ { njs_str("const a = 1; a &&= 2"),
+ njs_str("TypeError: assignment to constant variable") },
+ { njs_str("const a = 0; a &&= 2"),
+ njs_str("0") },
+
+ /* Logical assignment: getter/setter short-circuit. */
+
+ { njs_str("var log = '';"
+ "var o = {"
+ " get x() {log += 'g'; return 1},"
+ " set x(v) {log += 's'}"
+ "};"
+ "o.x ||= 2;"
+ "log"),
+ njs_str("g") },
+ { njs_str("var log = '';"
+ "var o = {"
+ " get x() {log += 'g'; return 0},"
+ " set x(v) {log += 's'}"
+ "};"
+ "o.x ||= 2;"
+ "log"),
+ njs_str("gs") },
+ { njs_str("var log = '';"
+ "var o = {"
+ " get x() {log += 'g'; return 0},"
+ " set x(v) {log += 's'}"
+ "};"
+ "o.x &&= 2;"
+ "log"),
+ njs_str("g") },
+ { njs_str("var log = '';"
+ "var o = {"
+ " get x() {log += 'g'; return 1},"
+ " set x(v) {log += 's'}"
+ "};"
+ "o.x &&= 2;"
+ "log"),
+ njs_str("gs") },
+
+ /* Logical assignment: non-lvalue error */
+
+ { njs_str("1 ||= 2"),
+ njs_str("ReferenceError: Invalid left-hand side in assignment") },
+ { njs_str("1 &&= 2"),
+ njs_str("ReferenceError: Invalid left-hand side in assignment") },
+
/* Optional chaining: property access. */
{ njs_str("var o = {a: 1}; o?.a"),