patch 8.2.4139: using freed memory in expression abbreviation

Problem:    Using freed memory if an expression abbreviation deletes the
            abbreviation.
Solution:   Do not access the pointer after evaluating the expression.
diff --git a/src/map.c b/src/map.c
index 6ad938d..b188e43 100644
--- a/src/map.c
+++ b/src/map.c
@@ -226,7 +226,7 @@
 #endif
 	int	    simplified)
 {
-    mapblock_T	*mp = ALLOC_ONE(mapblock_T);
+    mapblock_T	*mp = ALLOC_CLEAR_ONE(mapblock_T);
 
     if (mp == NULL)
 	return FAIL;
@@ -1515,6 +1515,12 @@
 	}
 	if (mp != NULL)
 	{
+	    int	noremap;
+	    int silent;
+#ifdef FEAT_EVAL
+	    int expr;
+#endif
+
 	    // Found a match:
 	    // Insert the rest of the abbreviation in typebuf.tb_buf[].
 	    // This goes from end to start.
@@ -1567,8 +1573,14 @@
 					// insert the last typed char
 		(void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
 	    }
+
+	    // copy values here, calling eval_map_expr() may make "mp" invalid!
+	    noremap = mp->m_noremap;
+	    silent = mp->m_silent;
 #ifdef FEAT_EVAL
-	    if (mp->m_expr)
+	    expr = mp->m_expr;
+
+	    if (expr)
 		s = eval_map_expr(mp, c);
 	    else
 #endif
@@ -1576,11 +1588,11 @@
 	    if (s != NULL)
 	    {
 					// insert the to string
-		(void)ins_typebuf(s, mp->m_noremap, 0, TRUE, mp->m_silent);
+		(void)ins_typebuf(s, noremap, 0, TRUE, silent);
 					// no abbrev. for these chars
 		typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
 #ifdef FEAT_EVAL
-		if (mp->m_expr)
+		if (expr)
 		    vim_free(s);
 #endif
 	    }
@@ -1590,7 +1602,7 @@
 	    if (has_mbyte)
 		len = clen;	// Delete characters instead of bytes
 	    while (len-- > 0)		// delete the from string
-		(void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
+		(void)ins_typebuf(tb, 1, 0, TRUE, silent);
 	    return TRUE;
 	}
     }
@@ -1601,6 +1613,7 @@
 /*
  * Evaluate the RHS of a mapping or abbreviations and take care of escaping
  * special characters.
+ * Careful: after this "mp" will be invalid if the mapping was deleted.
  */
     char_u *
 eval_map_expr(
diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim
index b170b58..a556362 100644
--- a/src/testdir/test_mapping.vim
+++ b/src/testdir/test_mapping.vim
@@ -704,6 +704,11 @@
   mapclear
 endfunc
 
+func GetAbbrText()
+  unabbr hola
+  return 'hello'
+endfunc
+
 " Test for <expr> in abbreviation
 func Test_expr_abbr()
   new
@@ -719,7 +724,14 @@
   call assert_equal('', getline(1))
   unabbr <expr> hte
 
-  close!
+  " evaluating the expression deletes the abbreviation
+  abbr <expr> hola GetAbbrText()
+  call assert_equal('GetAbbrText()', maparg('hola', 'i', '1'))
+  call feedkeys("ahola \<Esc>", 'xt')
+  call assert_equal('hello ', getline('.'))
+  call assert_equal('', maparg('hola', 'i', '1'))
+
+  bwipe!
 endfunc
 
 " Test for storing mappings in different modes in a vimrc file
diff --git a/src/version.c b/src/version.c
index 6198d41..07d5b81 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4139,
+/**/
     4138,
 /**/
     4137,