patch 9.0.2015: Vim9: does not handle islocked() from a method correctly

Problem:  Vim9: does not handle islocked() from a method correctly
Solution: Handle islocked() builtin from a method.

- Setup `lval_root` from `f_islocked()`.
- Add function `fill_exec_lval_root()` to get info about executing method.
- `sync_root` added in get_lval to handle method member access.
- Conservative approach to reference counting.

closes: #13309

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Ernie Rael <errael@raelity.com>
diff --git a/src/eval.c b/src/eval.c
index 8b26eb1..4da6246 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1050,11 +1050,14 @@
  *	execute_instructions: ISN_LOCKUNLOCK - sets lval_root from stack.
  */
     static void
-get_lval_root(lval_T *lp, lval_root_T *lr)
+fill_lval_from_lval_root(lval_T *lp, lval_root_T *lr)
 {
 #ifdef LOG_LOCKVAR
-    ch_log(NULL, "LKVAR: get_lval_root(): name %s", lp->ll_name);
+    ch_log(NULL, "LKVAR: fill_lval_from_lval_root(): name %s, tv %p",
+						lp->ll_name, (void*)lr->lr_tv);
 #endif
+    if (lr->lr_tv == NULL)
+	return;
     if (!lr->lr_is_arg && lr->lr_tv->v_type == VAR_CLASS)
     {
 	if (lr->lr_tv->vval.v_class != NULL)
@@ -1177,15 +1180,14 @@
 
 #ifdef LOG_LOCKVAR
     if (lval_root == NULL)
-	ch_log(NULL,
-	       "LKVAR: get_lval(): name %s, lval_root (nil)", name);
+	ch_log(NULL, "LKVAR: get_lval(): name: %s, lval_root (nil)", name);
     else
-	ch_log(NULL,
-	   "LKVAR: get_lval(): name %s, lr_tv %p lr_is_arg %d",
-	    name, (void*)lval_root->lr_tv, lval_root->lr_is_arg);
+	ch_log(NULL, "LKVAR: get_lval(): name: %s, lr_tv %p lr_is_arg %d",
+			name, (void*)lval_root->lr_tv, lval_root->lr_is_arg);
     char buf[80];
-    ch_log(NULL, "LKVAR:    ...: GLV flags %s",
+    ch_log(NULL, "LKVAR:    ...: GLV flags: %s",
 		    flags_tostring(flags, glv_flag_strings, buf, sizeof(buf)));
+    int log_sync_root_key = FALSE;
 #endif
 
     // Clear everything in "lp".
@@ -1324,20 +1326,26 @@
 	}
     }
 
-    // Without [idx] or .key we are done.
-    if ((*p != '[' && *p != '.'))
+    int sync_root = FALSE;
+    if (vim9script && lval_root != NULL)
+    {
+	cl_exec = lval_root->lr_cl_exec;
+	sync_root = lval_root->lr_sync_root;
+    }
+
+    // Without [idx] or .key we are done, unless doing sync_root.
+    if (*p != '[' && *p != '.' && (*name == NUL || !sync_root))
     {
 	if (lval_root != NULL)
-	    get_lval_root(lp, lval_root);
+	    fill_lval_from_lval_root(lp, lval_root);
 	return p;
     }
 
-    if (vim9script && lval_root != NULL)
+    if (vim9script && lval_root != NULL && lval_root->lr_tv != NULL)
     {
 	// using local variable
 	lp->ll_tv = lval_root->lr_tv;
 	v = NULL;
-	cl_exec = lval_root->lr_cl_exec;
     }
     else
     {
@@ -1367,7 +1375,7 @@
      */
     var1.v_type = VAR_UNKNOWN;
     var2.v_type = VAR_UNKNOWN;
-    while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
+    while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.') || sync_root)
     {
 	vartype_T v_type = lp->ll_tv->v_type;
 
@@ -1407,6 +1415,10 @@
 		emsg(_(e_slice_must_come_last));
 	    return NULL;
 	}
+#ifdef LOG_LOCKVAR
+	ch_log(NULL, "LKVAR: get_lval() loop: p: %s, type: %s", p,
+							vartype_name(v_type));
+#endif
 
 	if (vim9script && lp->ll_valtype == NULL
 		&& v != NULL
@@ -1417,11 +1429,29 @@
 
 	    // Vim9 script local variable: get the type
 	    if (sv != NULL)
+	    {
 		lp->ll_valtype = sv->sv_type;
+#ifdef LOG_LOCKVAR
+		ch_log(NULL, "LKVAR:    ... loop: vim9 assign type: %s",
+					vartype_name(lp->ll_valtype->tt_type));
+#endif
+	    }
 	}
 
 	len = -1;
-	if (*p == '.')
+	if (sync_root)
+	{
+	    // For example, the first token is a member variable name and
+	    // lp->ll_tv is a class/object.
+	    // Process it directly without looking for "[idx]" or ".name".
+	    key = name;
+	    sync_root = FALSE;  // only first time through
+#ifdef LOG_LOCKVAR
+	    log_sync_root_key = TRUE;
+	    ch_log(NULL, "LKVAR:    ... loop: name: %s, sync_root", name);
+#endif
+	}
+	else if (*p == '.')
 	{
 	    key = p + 1;
 	    for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
@@ -1512,6 +1542,17 @@
 	    // Skip to past ']'.
 	    ++p;
 	}
+#ifdef LOG_LOCKVAR
+	if (log_sync_root_key)
+	    ch_log(NULL, "LKVAR:    ... loop: p: %s, sync_root key: %s", p,
+									key);
+	else if (len == -1)
+	    ch_log(NULL, "LKVAR:    ... loop: p: %s, '[' key: %s", p,
+				empty1 ? ":" : (char*)tv_get_string(&var1));
+	else
+	    ch_log(NULL, "LKVAR:    ... loop: p: %s, '.' key: %s", p, key);
+	log_sync_root_key = FALSE;
+#endif
 
 	if (v_type == VAR_DICT)
 	{
@@ -1700,8 +1741,14 @@
 	    lp->ll_list = NULL;
 
 	    class_T *cl;
-	    if (v_type == VAR_OBJECT && lp->ll_tv->vval.v_object != NULL)
+	    if (v_type == VAR_OBJECT)
 	    {
+		if (lp->ll_tv->vval.v_object == NULL)
+		{
+		    if (!quiet)
+			emsg(_(e_using_null_object));
+		    return NULL;
+		}
 	        cl = lp->ll_tv->vval.v_object->obj_class;
 	        lp->ll_object = lp->ll_tv->vval.v_object;
 	    }