[git commit] awk: fix 'delete array[var--]' decrementing var twice

Denys Vlasenko vda.linux at googlemail.com
Sun Jan 7 00:19:08 UTC 2018


commit: https://git.busybox.net/busybox/commit/?id=6f4a785bd1bd0e6973b5c27b34b86655b1d7a602
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
evaluate                                            3395    3390      -5

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 editors/awk.c       | 54 ++++++++++++++++++++++++++++++-----------------------
 testsuite/awk.tests | 19 +++++++++++++++++++
 2 files changed, 50 insertions(+), 23 deletions(-)

diff --git a/editors/awk.c b/editors/awk.c
index d40c781..8f523ea 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -2514,6 +2514,32 @@ static var *evaluate(node *op, var *res)
 		op1 = op->l.n;
 		debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn);
 
+		/* "delete" is special:
+		 * "delete array[var--]" must evaluate index expr only once,
+		 * must not evaluate it in "execute inevitable things" part.
+		 */
+		if (XC(opinfo & OPCLSMASK) == XC(OC_DELETE)) {
+			uint32_t info = op1->info & OPCLSMASK;
+			var *v;
+
+			debug_printf_eval("DELETE\n");
+			if (info == OC_VAR) {
+				v = op1->l.v;
+			} else if (info == OC_FNARG) {
+				v = &fnargs[op1->l.aidx];
+			} else {
+				syntax_error(EMSG_NOT_ARRAY);
+			}
+			if (op1->r.n) { /* array ref? */
+				const char *s;
+				s = getvar_s(evaluate(op1->r.n, v1));
+				hash_remove(iamarray(v), s);
+			} else {
+				clear_array(iamarray(v));
+			}
+			goto next;
+		}
+
 		/* execute inevitable things */
 		if (opinfo & OF_RES1)
 			L.v = evaluate(op1, v1);
@@ -2621,28 +2647,7 @@ static var *evaluate(node *op, var *res)
 			break;
 		}
 
-		case XC( OC_DELETE ): {
-			uint32_t info = op1->info & OPCLSMASK;
-			var *v;
-
-			if (info == OC_VAR) {
-				v = op1->l.v;
-			} else if (info == OC_FNARG) {
-				v = &fnargs[op1->l.aidx];
-			} else {
-				syntax_error(EMSG_NOT_ARRAY);
-			}
-
-			if (op1->r.n) {
-				const char *s;
-				clrvar(L.v);
-				s = getvar_s(evaluate(op1->r.n, v1));
-				hash_remove(iamarray(v), s);
-			} else {
-				clear_array(iamarray(v));
-			}
-			break;
-		}
+		/* case XC( OC_DELETE ): - moved to happen before arg evaluation */
 
 		case XC( OC_NEWSOURCE ):
 			g_progname = op->l.new_progname;
@@ -2666,12 +2671,14 @@ static var *evaluate(node *op, var *res)
 		/* -- recursive node type -- */
 
 		case XC( OC_VAR ):
+			debug_printf_eval("VAR\n");
 			L.v = op->l.v;
 			if (L.v == intvar[NF])
 				split_f0();
 			goto v_cont;
 
 		case XC( OC_FNARG ):
+			debug_printf_eval("FNARG[%d]\n", op->l.aidx);
 			L.v = &fnargs[op->l.aidx];
  v_cont:
 			res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v;
@@ -3035,7 +3042,8 @@ static var *evaluate(node *op, var *res)
 
 		default:
 			syntax_error(EMSG_POSSIBLE_ERROR);
-		}
+		} /* switch */
+ next:
 		if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
 			op = op->a.n;
 		if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS)
diff --git a/testsuite/awk.tests b/testsuite/awk.tests
index 82937bc..ad0583a 100755
--- a/testsuite/awk.tests
+++ b/testsuite/awk.tests
@@ -261,6 +261,25 @@ end d
 " \
 	"" ""
 
+prg='
+BEGIN{
+cnt = 0
+a[cnt] = "zeroth"
+a[++cnt] = "first"
+delete a[cnt--]
+print cnt
+print "[0]:" a[0]
+print "[1]:" a[1]
+}'
+testing "awk 'delete a[v--]' evaluates v-- once" \
+	"awk '$prg'" \
+	"\
+0
+[0]:zeroth
+[1]:
+" \
+	"" ""
+
 testing "awk handles empty ()" \
 	"awk 'BEGIN {print()}' 2>&1" "awk: cmd. line:1: Empty sequence\n" "" ""
 


More information about the busybox-cvs mailing list