From c28076f882698ff9839e2b826a1c98515f5bcd81 Mon Sep 17 00:00:00 2001
From: Per Hedbor <ph@opera.com>
Date: Thu, 14 Aug 2014 17:32:45 +0200
Subject: [PATCH] Fixed some more cases of automap on the left side of an
 assignment.

It more or less works as one would expect now.
---
 src/array.c               | 54 +++++++++++++++++++++++++++++++++++++++
 src/array.h               |  4 +++
 src/docode.c              | 32 ++++++++++++++++++++++-
 src/interpret_functions.h | 21 +++++----------
 4 files changed, 96 insertions(+), 15 deletions(-)

diff --git a/src/array.c b/src/array.c
index a0aa7df036..72d692477e 100644
--- a/src/array.c
+++ b/src/array.c
@@ -2276,6 +2276,60 @@ PMOD_EXPORT struct array *append_array(struct array *a, struct svalue *s)
   return a;
 }
 
+/** Automap assignments
+ * This implements X[*] = ...[*]..
+ * Assign elements in a at @level to elements from b at the same @level.
+ * This will not actually modify any of the arrays, only change the
+ * values in them.
+ */
+void assign_array_level( struct array *a, struct array *b, int level )
+{
+    if( a->size != b->size )
+      /* this should not really happen. */
+        Pike_error("Source and destination differs in size in automap?!\n");
+
+    if( level > 1 )
+    {
+        /* recurse. */
+        unsigned int i;
+        for( i=0; i<a->size; i++ )
+        {
+            if( TYPEOF(a->item[i]) != PIKE_T_ARRAY )
+                Pike_error("Too many automap levels.\n");
+            if( TYPEOF(b->item[i]) != PIKE_T_ARRAY ) /* obscure messages much? */
+                Pike_error("Not enough levels of mapping in RHS\n");
+            assign_array_level( a->item[i].u.array, b->item[i].u.array, level-1 );
+        }
+    }
+    else
+        assign_svalues( a->item, b->item, a->size, a->type_field|b->type_field );
+}
+
+/* Assign all elemnts in a at level to b.
+ * This implements X[*] = expression without automap.
+ */
+void assign_array_level_value( struct array *a, struct svalue *b, int level )
+{
+    unsigned int i;
+    if( level > 1 )
+    {
+        /* recurse. */
+        for( i=0; i<a->size; i++ )
+        {
+            if( TYPEOF(a->item[i]) != PIKE_T_ARRAY )
+                Pike_error("Too many automap levels.\n");
+            assign_array_level_value( a->item[i].u.array, b, level-1 );
+        }
+    }
+    else
+    {
+        if( a->type_field & BIT_REF_TYPES )  free_mixed_svalues( a->item, a->size );
+        if( REFCOUNTED_TYPE(TYPEOF(*b)) )     *b->u.refs+=a->size;
+        for( i=0; i<a->size; i++)
+            a->item[i] = *b;
+    }
+}
+
 typedef char *(* explode_searchfunc)(void *,void *,size_t);
 
 /** Explode a string into an array by a delimiter.
diff --git a/src/array.h b/src/array.h
index 0ceaee5313..1333f70930 100644
--- a/src/array.h
+++ b/src/array.h
@@ -211,6 +211,10 @@ void debug_dump_array(struct array *a);
 void count_memory_in_arrays(size_t *num_, size_t *size_);
 PMOD_EXPORT struct array *explode_array(struct array *a, struct array *b);
 PMOD_EXPORT struct array *implode_array(struct array *a, struct array *b);
+
+/* Automap internals. */
+void assign_array_level_value( struct array *a, struct svalue *b, int level );
+void assign_array_level( struct array *a, struct array *b, int level );
 /* Prototypes end here */
 
 #define array_get_flags(a) ((a)->flags)
diff --git a/src/docode.c b/src/docode.c
index 8910c521be..edbd404558 100644
--- a/src/docode.c
+++ b/src/docode.c
@@ -430,6 +430,24 @@ static INT32 count_cases(node *n)
   }
 }
 
+static int has_automap(node *n)
+{
+  if(!n) return 0;
+  switch(n->token)
+  {
+  case F_AUTO_MAP_MARKER:
+  case F_AUTO_MAP:
+    return 1;
+
+  default:
+    if(car_is_node(n) && has_automap(CAR(n)) )
+      return 1;
+    if( cdr_is_node(n) && has_automap(CDR(n)) )
+      return 1;
+  }
+  return 0;
+}
+
 
 int generate_call_function(node *n)
 {
@@ -1173,7 +1191,19 @@ static int do_docode2(node *n, int flags)
         do_docode(lval,0); /* note: not lvalue */
         if(do_docode(CAR(n),0)!=1)
             yyerror("RHS is void!");
-        emit1(F_ASSIGN_INDICES,depth);
+
+        if( CAR(n)->token == F_AUTO_MAP_MARKER ||
+            CAR(n)->token == F_AUTO_MAP ||
+            /* Well, hello there... ;) */
+            /* This is what is generated by a[*] += 10 and such. */
+            (CAR(n)->token == F_SOFT_CAST &&
+             has_automap(CAR(n))))
+        {
+          emit1(F_ASSIGN_INDICES,depth);
+        }
+        else
+          emit1(F_ASSIGN_ALL_INDICES,depth);
+
         if( flags & DO_POP )
             emit0( F_POP_VALUE );
         return !(flags&DO_POP);
diff --git a/src/interpret_functions.h b/src/interpret_functions.h
index 994b80790d..f7c52401cd 100644
--- a/src/interpret_functions.h
+++ b/src/interpret_functions.h
@@ -1102,25 +1102,18 @@ OPCODE0(F_ASSIGN, "assign", I_UPDATE_SP, {
 });
 
 OPCODE1(F_ASSIGN_INDICES, "assign[]", I_UPDATE_SP, {
-  LOCAL_VAR(struct array *arr);
-  LOCAL_VAR(struct array *from);
-  LOCAL_VAR(int i);
-
-  /* Note: All thse checks are presumably fairly pointless. */
   if(TYPEOF(Pike_sp[-2]) != PIKE_T_ARRAY )
       PIKE_ERROR("[*]=", "Destination is not an array.\n", Pike_sp, 1);
-
   if(TYPEOF(Pike_sp[-1]) != PIKE_T_ARRAY )
       PIKE_ERROR("[*]=", "Source is not an array.\n", Pike_sp-1, 1);
+  assign_array_level( Pike_sp[-2].u.array, Pike_sp[-1].u.array, arg1 );
+  pop_stack(); /* leaves arr on stack. */
+});
 
-  arr  = Pike_sp[-2].u.array;
-  from = Pike_sp[-1].u.array;
-
-  if( arr->size != from->size )
-      Pike_error("Source and destination differs in size in automap.\n");
-
-  assign_svalues(arr->item,from->item,arr->size,arr->type_field|from->type_field);
-
+OPCODE1(F_ASSIGN_ALL_INDICES, "assign[*]", I_UPDATE_SP, {
+  if(TYPEOF(Pike_sp[-2]) != PIKE_T_ARRAY )
+      PIKE_ERROR("[*]=", "Destination is not an array.\n", Pike_sp, 1);
+  assign_array_level_value( Pike_sp[-2].u.array, Pike_sp-1, arg1 );
   pop_stack(); /* leaves arr on stack. */
 });
 
-- 
GitLab