summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2024-05-12 09:24:35 +0200
committerChristian Brabandt <cb@256bit.org>2024-05-12 09:24:35 +0200
commit4ceb4dc825854032eed423ec1fc372317d3420bf (patch)
tree29bfc9d4f09c707b369b861cd030744898874692
parentd01e699348dc98109860feb38e5fc24c6dc3d91d (diff)
patch 9.1.0411: too long functions in eval.cv9.1.0411
Problem: too long functions in eval.c Solution: refactor functions (Yegappan Lakshmanan) closes: #14755 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
-rw-r--r--src/eval.c1240
-rw-r--r--src/version.c2
2 files changed, 753 insertions, 489 deletions
diff --git a/src/eval.c b/src/eval.c
index b547143fe3..93dc09192e 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -250,93 +250,146 @@ eval_expr_get_funccal(typval_T *expr, typval_T *rettv)
}
/*
- * Evaluate an expression, which can be a function, partial or string.
+ * Evaluate a partial.
* Pass arguments "argv[argc]".
- * If "want_func" is TRUE treat a string as a function name, not an expression.
* "fc_arg" is from eval_expr_get_funccal() or NULL;
* Return the result in "rettv" and OK or FAIL.
*/
- int
-eval_expr_typval(
- typval_T *expr,
- int want_func,
- typval_T *argv,
- int argc,
- funccall_T *fc_arg,
- typval_T *rettv)
+ static int
+eval_expr_partial(
+ typval_T *expr,
+ typval_T *argv,
+ int argc,
+ funccall_T *fc_arg,
+ typval_T *rettv)
{
- char_u *s;
- char_u buf[NUMBUFLEN];
- funcexe_T funcexe;
+ partial_T *partial = expr->vval.v_partial;
- if (expr->v_type == VAR_PARTIAL)
+ if (partial == NULL)
+ return FAIL;
+
+ if (partial->pt_func != NULL
+ && partial->pt_func->uf_def_status != UF_NOT_COMPILED)
{
- partial_T *partial = expr->vval.v_partial;
+ funccall_T *fc = fc_arg != NULL ? fc_arg
+ : create_funccal(partial->pt_func, rettv);
+ int r;
- if (partial == NULL)
+ if (fc == NULL)
return FAIL;
- if (partial->pt_func != NULL
- && partial->pt_func->uf_def_status != UF_NOT_COMPILED)
- {
- funccall_T *fc = fc_arg != NULL ? fc_arg
- : create_funccal(partial->pt_func, rettv);
- int r;
-
- if (fc == NULL)
- return FAIL;
-
- // Shortcut to call a compiled function with minimal overhead.
- r = call_def_function(partial->pt_func, argc, argv,
- DEF_USE_PT_ARGV, partial, NULL, fc, rettv);
- if (fc_arg == NULL)
- remove_funccal();
- if (r == FAIL)
- return FAIL;
- }
- else
- {
- s = partial_name(partial);
- if (s == NULL || *s == NUL)
- return FAIL;
- CLEAR_FIELD(funcexe);
- funcexe.fe_evaluate = TRUE;
- funcexe.fe_partial = partial;
- if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
- return FAIL;
- }
- }
- else if (expr->v_type == VAR_INSTR)
- {
- return exe_typval_instr(expr, rettv);
+ // Shortcut to call a compiled function with minimal overhead.
+ r = call_def_function(partial->pt_func, argc, argv, DEF_USE_PT_ARGV,
+ partial, NULL, fc, rettv);
+ if (fc_arg == NULL)
+ remove_funccal();
+ if (r == FAIL)
+ return FAIL;
}
- else if (expr->v_type == VAR_FUNC || want_func)
+ else
{
- s = expr->v_type == VAR_FUNC
- ? expr->vval.v_string
- : tv_get_string_buf_chk_strict(expr, buf, in_vim9script());
+ char_u *s = partial_name(partial);
+ funcexe_T funcexe;
+
if (s == NULL || *s == NUL)
return FAIL;
+
CLEAR_FIELD(funcexe);
funcexe.fe_evaluate = TRUE;
+ funcexe.fe_partial = partial;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
return FAIL;
}
+
+ return OK;
+}
+
+/*
+ * Evaluate an expression which is a function.
+ * Pass arguments "argv[argc]".
+ * Return the result in "rettv" and OK or FAIL.
+ */
+ static int
+eval_expr_func(
+ typval_T *expr,
+ typval_T *argv,
+ int argc,
+ typval_T *rettv)
+{
+ funcexe_T funcexe;
+ char_u buf[NUMBUFLEN];
+ char_u *s;
+
+ if (expr->v_type == VAR_FUNC)
+ s = expr->vval.v_string;
else
- {
s = tv_get_string_buf_chk_strict(expr, buf, in_vim9script());
- if (s == NULL)
- return FAIL;
- s = skipwhite(s);
- if (eval1_emsg(&s, rettv, NULL) == FAIL)
- return FAIL;
- if (*skipwhite(s) != NUL) // check for trailing chars after expr
- {
- clear_tv(rettv);
- semsg(_(e_invalid_expression_str), s);
- return FAIL;
- }
+ if (s == NULL || *s == NUL)
+ return FAIL;
+
+ CLEAR_FIELD(funcexe);
+ funcexe.fe_evaluate = TRUE;
+ if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
+ return FAIL;
+
+ return OK;
+}
+
+/*
+ * Evaluate an expression, which is a string.
+ * Return the result in "rettv" and OK or FAIL.
+ */
+ static int
+eval_expr_string(
+ typval_T *expr,
+ typval_T *rettv)
+{
+ char_u *s;
+ char_u buf[NUMBUFLEN];
+
+ s = tv_get_string_buf_chk_strict(expr, buf, in_vim9script());
+ if (s == NULL)
+ return FAIL;
+
+ s = skipwhite(s);
+ if (eval1_emsg(&s, rettv, NULL) == FAIL)
+ return FAIL;
+
+ if (*skipwhite(s) != NUL) // check for trailing chars after expr
+ {
+ clear_tv(rettv);
+ semsg(_(e_invalid_expression_str), s);
+ return FAIL;
}
+
+ return OK;
+}
+
+/*
+ * Evaluate an expression, which can be a function, partial or string.
+ * Pass arguments "argv[argc]".
+ * If "want_func" is TRUE treat a string as a function name, not an expression.
+ * "fc_arg" is from eval_expr_get_funccal() or NULL;
+ * Return the result in "rettv" and OK or FAIL.
+ */
+ int
+eval_expr_typval(
+ typval_T *expr,
+ int want_func,
+ typval_T *argv,
+ int argc,
+ funccall_T *fc_arg,
+ typval_T *rettv)
+{
+ if (expr->v_type == VAR_PARTIAL)
+ return eval_expr_partial(expr, argv, argc, fc_arg, rettv);
+ else if (expr->v_type == VAR_INSTR)
+ return exe_typval_instr(expr, rettv);
+ else if (expr->v_type == VAR_FUNC || want_func)
+ return eval_expr_func(expr, argv, argc, rettv);
+ else
+ return eval_expr_string(expr, rettv);
+
return OK;
}
@@ -1117,41 +1170,40 @@ get_lval_check_access(
ch_log(NULL, "LKVAR: get_lval_check_access(), cl_exec %p, cl %p, %c",
(void*)cl_exec, (void*)cl, *p);
#endif
- if (cl_exec == NULL || cl_exec != cl)
+ if (cl_exec != NULL && cl_exec == cl)
+ return OK;
+
+ char *msg = NULL;
+ switch (om->ocm_access)
{
- char *msg = NULL;
- switch (om->ocm_access)
- {
- case VIM_ACCESS_PRIVATE:
- msg = e_cannot_access_protected_variable_str;
+ case VIM_ACCESS_PRIVATE:
+ msg = e_cannot_access_protected_variable_str;
+ break;
+ case VIM_ACCESS_READ:
+ // If [idx] or .key following, read only OK.
+ if (*p == '[' || *p == '.')
break;
- case VIM_ACCESS_READ:
- // If [idx] or .key following, read only OK.
- if (*p == '[' || *p == '.')
- break;
- if ((flags & GLV_READ_ONLY) == 0)
+ if ((flags & GLV_READ_ONLY) == 0)
+ {
+ if (IS_ENUM(cl))
{
- if (IS_ENUM(cl))
- {
- if (om->ocm_type->tt_type == VAR_OBJECT)
- semsg(_(e_enumvalue_str_cannot_be_modified),
- cl->class_name, om->ocm_name);
- else
- msg = e_variable_is_not_writable_str;
- }
+ if (om->ocm_type->tt_type == VAR_OBJECT)
+ semsg(_(e_enumvalue_str_cannot_be_modified),
+ cl->class_name, om->ocm_name);
else
msg = e_variable_is_not_writable_str;
}
- break;
- case VIM_ACCESS_ALL:
- break;
- }
- if (msg != NULL)
- {
- emsg_var_cl_define(msg, om->ocm_name, 0, cl);
- return FAIL;
- }
-
+ else
+ msg = e_variable_is_not_writable_str;
+ }
+ break;
+ case VIM_ACCESS_ALL:
+ break;
+ }
+ if (msg != NULL)
+ {
+ emsg_var_cl_define(msg, om->ocm_name, 0, cl);
+ return FAIL;
}
return OK;
}
@@ -1230,6 +1282,382 @@ failed:
return rc == OK ? p : NULL;
}
+typedef enum {
+ GLV_FAIL,
+ GLV_OK,
+ GLV_STOP
+} glv_status_T;
+
+/*
+ * Get an Dict lval variable that can be assigned a value to: "name",
+ * "name[expr]", "name[expr][expr]", "name.key", "name.key[expr]" etc.
+ * "name" points to the start of the name.
+ * If "rettv" is not NULL it points to the value to be assigned.
+ * "unlet" is TRUE for ":unlet": slightly different behavior when something is
+ * wrong; must end in space or cmd separator.
+ *
+ * flags:
+ * GLV_QUIET: do not give error messages
+ * GLV_READ_ONLY: will not change the variable
+ * GLV_NO_AUTOLOAD: do not use script autoloading
+ *
+ * The Dict is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on
+ * failure. Returns GLV_STOP to stop processing the characters following
+ * 'key_end'.
+ */
+ static int
+get_lval_dict_item(
+ char_u *name,
+ lval_T *lp,
+ char_u *key,
+ int len,
+ char_u **key_end,
+ typval_T *var1,
+ int flags,
+ int unlet,
+ typval_T *rettv)
+{
+ int quiet = flags & GLV_QUIET;
+ char_u *p = *key_end;
+
+ if (len == -1)
+ {
+ // "[key]": get key from "var1"
+ key = tv_get_string_chk(var1); // is number or string
+ if (key == NULL)
+ {
+ clear_tv(var1);
+ return GLV_FAIL;
+ }
+ }
+ lp->ll_list = NULL;
+ lp->ll_object = NULL;
+ lp->ll_class = NULL;
+
+ // a NULL dict is equivalent with an empty dict
+ if (lp->ll_tv->vval.v_dict == NULL)
+ {
+ lp->ll_tv->vval.v_dict = dict_alloc();
+ if (lp->ll_tv->vval.v_dict == NULL)
+ {
+ clear_tv(var1);
+ return GLV_FAIL;
+ }
+ ++lp->ll_tv->vval.v_dict->dv_refcount;
+ }
+ lp->ll_dict = lp->ll_tv->vval.v_dict;
+
+ lp->ll_di = dict_find(lp->ll_dict, key, len);
+
+ // When assigning to a scope dictionary check that a function and
+ // variable name is valid (only variable name unless it is l: or
+ // g: dictionary). Disallow overwriting a builtin function.
+ if (rettv != NULL && lp->ll_dict->dv_scope != 0)
+ {
+ int prevval;
+
+ if (len != -1)
+ {
+ prevval = key[len];
+ key[len] = NUL;
+ }
+ else
+ prevval = 0; // avoid compiler warning
+ int wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE
+ && (rettv->v_type == VAR_FUNC
+ || rettv->v_type == VAR_PARTIAL)
+ && var_wrong_func_name(key, lp->ll_di == NULL))
+ || !valid_varname(key, -1, TRUE);
+ if (len != -1)
+ key[len] = prevval;
+ if (wrong)
+ {
+ clear_tv(var1);
+ return GLV_FAIL;
+ }
+ }
+
+ if (lp->ll_valtype != NULL)
+ // use the type of the member
+ lp->ll_valtype = lp->ll_valtype->tt_member;
+
+ if (lp->ll_di == NULL)
+ {
+ // Can't add "v:" or "a:" variable.
+ if (lp->ll_dict == get_vimvar_dict()
+ || &lp->ll_dict->dv_hashtab == get_funccal_args_ht())
+ {
+ semsg(_(e_illegal_variable_name_str), name);
+ clear_tv(var1);
+ return GLV_FAIL;
+ }
+
+ // Key does not exist in dict: may need to add it.
+ if (*p == '[' || *p == '.' || unlet)
+ {
+ if (!quiet)
+ semsg(_(e_key_not_present_in_dictionary_str), key);
+ clear_tv(var1);
+ return GLV_FAIL;
+ }
+ if (len == -1)
+ lp->ll_newkey = vim_strsave(key);
+ else
+ lp->ll_newkey = vim_strnsave(key, len);
+ clear_tv(var1);
+ if (lp->ll_newkey == NULL)
+ p = NULL;
+
+ *key_end = p;
+ return GLV_STOP;
+ }
+ // existing variable, need to check if it can be changed
+ else if ((flags & GLV_READ_ONLY) == 0
+ && (var_check_ro(lp->ll_di->di_flags, name, FALSE)
+ || var_check_lock(lp->ll_di->di_flags, name, FALSE)))
+ {
+ clear_tv(var1);
+ return GLV_FAIL;
+ }
+
+ clear_tv(var1);
+ lp->ll_tv = &lp->ll_di->di_tv;
+
+ return GLV_OK;
+}
+
+/*
+ * Get an blob lval variable that can be assigned a value to: "name",
+ * "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc.
+ *
+ * 'var1' specifies the starting blob index and 'var2' specifies the ending
+ * blob index. If the first index is not specified in a range, then 'empty1'
+ * is TRUE. If 'quiet' is TRUE, then error messages are not displayed for
+ * invalid indexes.
+ *
+ * The blob is returned in 'lp'. Returns OK on success and FAIL on failure.
+ */
+ static int
+get_lval_blob(
+ lval_T *lp,
+ typval_T *var1,
+ typval_T *var2,
+ int empty1,
+ int quiet)
+{
+ long bloblen = blob_len(lp->ll_tv->vval.v_blob);
+
+ // Get the number and item for the only or first index of the List.
+ if (empty1)
+ lp->ll_n1 = 0;
+ else
+ // is number or string
+ lp->ll_n1 = (long)tv_get_number(var1);
+ clear_tv(var1);
+
+ if (check_blob_index(bloblen, lp->ll_n1, quiet) == FAIL)
+ {
+ clear_tv(var2);
+ return FAIL;
+ }
+ if (lp->ll_range && !lp->ll_empty2)
+ {
+ lp->ll_n2 = (long)tv_get_number(var2);
+ clear_tv(var2);
+ if (check_blob_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL)
+ return FAIL;
+ }
+
+ if (!lp->ll_range)
+ // Indexing a single byte in a blob. So the rhs type is a
+ // number.
+ lp->ll_valtype = &t_number;
+
+ lp->ll_blob = lp->ll_tv->vval.v_blob;
+ lp->ll_tv = NULL;
+
+ return OK;
+}
+
+/*
+ * Get a List lval variable that can be assigned a value to: "name",
+ * "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc.
+ *
+ * 'var1' specifies the starting List index and 'var2' specifies the ending
+ * List index. If the first index is not specified in a range, then 'empty1'
+ * is TRUE. If 'quiet' is TRUE, then error messages are not displayed for
+ * invalid indexes.
+ *
+ * The List is returned in 'lp'. Returns OK on success and FAIL on failure.
+ */
+ static int
+get_lval_list(
+ lval_T *lp,
+ typval_T *var1,
+ typval_T *var2,
+ int empty1,
+ int flags,
+ int quiet)
+{
+ /*
+ * Get the number and item for the only or first index of the List.
+ */
+ if (empty1)
+ lp->ll_n1 = 0;
+ else
+ // is number or string
+ lp->ll_n1 = (long)tv_get_number(var1);
+ clear_tv(var1);
+
+ lp->ll_dict = NULL;
+ lp->ll_object = NULL;
+ lp->ll_class = NULL;
+ lp->ll_list = lp->ll_tv->vval.v_list;
+ lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1,
+ (flags & GLV_ASSIGN_WITH_OP) == 0, quiet);
+ if (lp->ll_li == NULL)
+ {
+ clear_tv(var2);
+ return FAIL;
+ }
+
+ if (lp->ll_valtype != NULL && !lp->ll_range)
+ // use the type of the member
+ lp->ll_valtype = lp->ll_valtype->tt_member;
+
+ /*
+ * May need to find the item or absolute index for the second
+ * index of a range.
+ * When no index given: "lp->ll_empty2" is TRUE.
+ * Otherwise "lp->ll_n2" is set to the second index.
+ */
+ if (lp->ll_range && !lp->ll_empty2)
+ {
+ lp->ll_n2 = (long)tv_get_number(var2);
+ // is number or string
+ clear_tv(var2);
+ if (check_range_index_two(lp->ll_list,
+ &lp->ll_n1, lp->ll_li, &lp->ll_n2, quiet) == FAIL)
+ return FAIL;
+ }
+
+ lp->ll_tv = &lp->ll_li->li_tv;
+
+ return OK;
+}
+
+/*
+ * Get a Class or Object lval variable that can be assigned a value to:
+ * "name", "name.key", "name.key[expr]" etc.
+ *
+ * 'cl_exec' is the class that is executing, or NULL. 'v_type' is VAR_CLASS or
+ * VAR_OBJECT. 'key' points to the member variable name and 'key_end' points
+ * to the character after 'key'. If 'quiet' is TRUE, then error messages
+ * are not displayed for invalid indexes.
+ *
+ * The Class or Object is returned in 'lp'. Returns OK on success and FAIL on
+ * failure.
+ */
+ static int
+get_lval_class_or_obj(
+ class_T *cl_exec,
+ vartype_T v_type,
+ lval_T *lp,
+ char_u *key,
+ char_u *key_end,
+ int flags,
+ int quiet)
+{
+ lp->ll_dict = NULL;
+ lp->ll_list = NULL;
+
+ class_T *cl;
+ if (v_type == VAR_OBJECT)
+ {
+ if (lp->ll_tv->vval.v_object == NULL)
+ {
+ if (!quiet)
+ emsg(_(e_using_null_object));
+ return FAIL;
+ }
+ cl = lp->ll_tv->vval.v_object->obj_class;
+ lp->ll_object = lp->ll_tv->vval.v_object;
+ }
+ else
+ {
+ cl = lp->ll_tv->vval.v_class;
+ lp->ll_object = NULL;
+ }
+ lp->ll_class = cl;
+
+ // TODO: what if class is NULL?
+ if (cl != NULL)
+ {
+ lp->ll_valtype = NULL;
+
+ if (flags & GLV_PREFER_FUNC)
+ {
+ // First look for a function with this name.
+ // round 1: class functions (skipped for an object)
+ // round 2: object methods
+ for (int round = v_type == VAR_OBJECT ? 2 : 1;
+ round <= 2; ++round)
+ {
+ int m_idx;
+ ufunc_T *fp;
+
+ fp = method_lookup(cl,
+ round == 1 ? VAR_CLASS : VAR_OBJECT,
+ key, key_end - key, &m_idx);
+ lp->ll_oi = m_idx;
+ if (fp != NULL)
+ {
+ lp->ll_ufunc = fp;
+ lp->ll_valtype = fp->uf_func_type;
+ break;
+ }
+ }
+ }
+
+ if (lp->ll_valtype == NULL)
+ {
+ int m_idx;
+ ocmember_T *om
+ = member_lookup(cl, v_type, key, key_end - key, &m_idx);
+ lp->ll_oi = m_idx;
+ if (om != NULL)
+ {
+ if (get_lval_check_access(cl_exec, cl, om,
+ key_end, flags) == FAIL)
+ return FAIL;
+
+ // When lhs is used to modify the variable, check it is
+ // not a read-only variable.
+ if ((flags & GLV_READ_ONLY) == 0
+ && (*key_end != '.' && *key_end != '[')
+ && oc_var_check_ro(cl, om))
+ return FAIL;
+
+ lp->ll_valtype = om->ocm_type;
+
+ if (v_type == VAR_OBJECT)
+ lp->ll_tv = ((typval_T *)(
+ lp->ll_tv->vval.v_object + 1)) + m_idx;
+ else
+ lp->ll_tv = &cl->class_members_tv[m_idx];
+ }
+ }
+
+ if (lp->ll_valtype == NULL)
+ {
+ member_not_found_msg(cl, v_type, key, key_end - key);
+ return FAIL;
+ }
+ }
+
+ return OK;
+}
+
/*
* Get an lval: variable, Dict item or List item that can be assigned a value
* to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
@@ -1614,279 +2042,32 @@ get_lval(
if (v_type == VAR_DICT)
{
- if (len == -1)
- {
- // "[key]": get key from "var1"
- key = tv_get_string_chk(&var1); // is number or string
- if (key == NULL)
- {
- clear_tv(&var1);
- return NULL;
- }
- }
- lp->ll_list = NULL;
- lp->ll_object = NULL;
- lp->ll_class = NULL;
+ glv_status_T glv_status;
- // a NULL dict is equivalent with an empty dict
- if (lp->ll_tv->vval.v_dict == NULL)
- {
- lp->ll_tv->vval.v_dict = dict_alloc();
- if (lp->ll_tv->vval.v_dict == NULL)
- {
- clear_tv(&var1);
- return NULL;
- }
- ++lp->ll_tv->vval.v_dict->dv_refcount;
- }
- lp->ll_dict = lp->ll_tv->vval.v_dict;
-
- lp->ll_di = dict_find(lp->ll_dict, key, len);
-
- // When assigning to a scope dictionary check that a function and
- // variable name is valid (only variable name unless it is l: or
- // g: dictionary). Disallow overwriting a builtin function.
- if (rettv != NULL && lp->ll_dict->dv_scope != 0)
- {
- int prevval;
-
- if (len != -1)
- {
- prevval = key[len];
- key[len] = NUL;
- }
- else
- prevval = 0; // avoid compiler warning
- int wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE
- && (rettv->v_type == VAR_FUNC
- || rettv->v_type == VAR_PARTIAL)
- && var_wrong_func_name(key, lp->ll_di == NULL))
- || !valid_varname(key, -1, TRUE);
- if (len != -1)
- key[len] = prevval;
- if (wrong)
- {
- clear_tv(&var1);
- return NULL;
- }
- }
-
- if (lp->ll_valtype != NULL)
- // use the type of the member
- lp->ll_valtype = lp->ll_valtype->tt_member;
-
- if (lp->ll_di == NULL)
- {
- // Can't add "v:" or "a:" variable.
- if (lp->ll_dict == get_vimvar_dict()
- || &lp->ll_dict->dv_hashtab == get_funccal_args_ht())
- {
- semsg(_(e_illegal_variable_name_str), name);
- clear_tv(&var1);
- return NULL;
- }
-
- // Key does not exist in dict: may need to add it.
- if (*p == '[' || *p == '.' || unlet)
- {
- if (!quiet)
- semsg(_(e_key_not_present_in_dictionary_str), key);
- clear_tv(&var1);
- return NULL;
- }
- if (len == -1)
- lp->ll_newkey = vim_strsave(key);
- else
- lp->ll_newkey = vim_strnsave(key, len);
- clear_tv(&var1);
- if (lp->ll_newkey == NULL)
- p = NULL;
- break;
- }
- // existing variable, need to check if it can be changed
- else if ((flags & GLV_READ_ONLY) == 0
- && (var_check_ro(lp->ll_di->di_flags, name, FALSE)
- || var_check_lock(lp->ll_di->di_flags, name, FALSE)))
- {
- clear_tv(&var1);
+ glv_status = get_lval_dict_item(name, lp, key, len, &p, &var1,
+ flags, unlet, rettv);
+ if (glv_status == GLV_FAIL)
return NULL;
- }
-
- clear_tv(&var1);
- lp->ll_tv = &lp->ll_di->di_tv;
+ if (glv_status == GLV_STOP)
+ break;
}
else if (v_type == VAR_BLOB)
{
- long bloblen = blob_len(lp->ll_tv->vval.v_blob);
-
- /*
- * Get the number and item for the only or first index of the List.
- */
- if (empty1)
- lp->ll_n1 = 0;
- else
- // is number or string
- lp->ll_n1 = (long)tv_get_number(&var1);
- clear_tv(&var1);
-
- if (check_blob_index(bloblen, lp->ll_n1, quiet) == FAIL)
- {
- clear_tv(&var2);
+ if (get_lval_blob(lp, &var1, &var2, empty1, quiet) == FAIL)
return NULL;
- }
- if (lp->ll_range && !lp->ll_empty2)
- {
- lp->ll_n2 = (long)tv_get_number(&var2);
- clear_tv(&var2);
- if (check_blob_range(bloblen, lp->ll_n1, lp->ll_n2, quiet)
- == FAIL)
- return NULL;
- }
-
- if (!lp->ll_range)
- // Indexing a single byte in a blob. So the rhs type is a
- // number.
- lp->ll_valtype = &t_number;
- lp->ll_blob = lp->ll_tv->vval.v_blob;
- lp->ll_tv = NULL;
break;
}
else if (v_type == VAR_LIST)
{
- /*
- * Get the number and item for the only or first index of the List.
- */
- if (empty1)
- lp->ll_n1 = 0;
- else
- // is number or string
- lp->ll_n1 = (long)tv_get_number(&var1);
- clear_tv(&var1);
-
- lp->ll_dict = NULL;
- lp->ll_object = NULL;
- lp->ll_class = NULL;
- lp->ll_list = lp->ll_tv->vval.v_list;
- lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1,
- (flags & GLV_ASSIGN_WITH_OP) == 0, quiet);
- if (lp->ll_li == NULL)
- {
- clear_tv(&var2);
+ if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL)
return NULL;
- }
-
- if (lp->ll_valtype != NULL && !lp->ll_range)
- // use the type of the member
- lp->ll_valtype = lp->ll_valtype->tt_member;
-
- /*
- * May need to find the item or absolute index for the second
- * index of a range.
- * When no index given: "lp->ll_empty2" is TRUE.
- * Otherwise "lp->ll_n2" is set to the second index.
- */
- if (lp->ll_range && !lp->ll_empty2)
- {
- lp->ll_n2 = (long)tv_get_number(&var2);
- // is number or string
- clear_tv(&var2);
- if (check_range_index_two(lp->ll_list,
- &lp->ll_n1, lp->ll_li,
- &lp->ll_n2, quiet) == FAIL)
- return NULL;
- }
-
- lp->ll_tv = &lp->ll_li->li_tv;
}
else // v_type == VAR_CLASS || v_type == VAR_OBJECT
{
- lp->ll_dict = NULL;
- lp->ll_list = NULL;
-
- class_T *cl;
- 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;
- }
- else
- {
- cl = lp->ll_tv->vval.v_class;
- lp->ll_object = NULL;
- }
- lp->ll_class = cl;
-
- // TODO: what if class is NULL?
- if (cl != NULL)
- {
- lp->ll_valtype = NULL;
-
- if (flags & GLV_PREFER_FUNC)
- {
- // First look for a function with this name.
- // round 1: class functions (skipped for an object)
- // round 2: object methods
- for (int round = v_type == VAR_OBJECT ? 2 : 1;
- round <= 2; ++round)
- {
- int m_idx;
- ufunc_T *fp;
-
- fp = method_lookup(cl,
- round == 1 ? VAR_CLASS : VAR_OBJECT,
- key, p - key, &m_idx);
- lp->ll_oi = m_idx;
- if (fp != NULL)
- {
- lp->ll_ufunc = fp;
- lp->ll_valtype = fp->uf_func_type;
- break;
- }
- }
- }
-
- if (lp->ll_valtype == NULL)
- {
- int m_idx;
- ocmember_T *om
- = member_lookup(cl, v_type, key, p - key, &m_idx);
- lp->ll_oi = m_idx;
- if (om != NULL)
- {
- if (get_lval_check_access(cl_exec, cl, om,
- p, flags) == FAIL)
- return NULL;
-
- // When lhs is used to modify the variable, check it is
- // not a read-only variable.
- if ((flags & GLV_READ_ONLY) == 0
- && (*p != '.' && *p != '[')
- && oc_var_check_ro(cl, om))
- return NULL;
-
- lp->ll_valtype = om->ocm_type;
-
- if (v_type == VAR_OBJECT)
- lp->ll_tv = ((typval_T *)(
- lp->ll_tv->vval.v_object + 1)) + m_idx;
- else
- lp->ll_tv = &cl->class_members_tv[m_idx];
- }
- }
-
- if (lp->ll_valtype == NULL)
- {
- member_not_found_msg(cl, v_type, key, p - key);
- return NULL;
- }
- }
+ if (get_lval_class_or_obj(cl_exec, v_type, lp, key, p, flags,
+ quiet) == FAIL)
+ return FAIL;
}
}
@@ -2090,159 +2271,240 @@ set_var_lval(
}
/*
- * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
- * and "tv1 .= tv2"
+ * Handle "blob1 += blob2".
* Returns OK or FAIL.
*/
- int
-tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
+ static int
+tv_op_blob(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ if (*op != '+' || tv2->v_type != VAR_BLOB)
+ return FAIL;
+
+ // Blob += Blob
+ if (tv1->vval.v_blob == NULL || tv2->vval.v_blob == NULL)
+ return OK;
+
+ blob_T *b1 = tv1->vval.v_blob;
+ blob_T *b2 = tv2->vval.v_blob;
+ int len = blob_len(b2);
+
+ for (int i = 0; i < len; i++)
+ ga_append(&b1->bv_ga, blob_get(b2, i));
+
+ return OK;
+}
+
+/*
+ * Handle "list1 += list2".
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_list(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ if (*op != '+' || tv2->v_type != VAR_LIST)
+ return FAIL;
+
+ // List += List
+ if (tv2->vval.v_list == NULL)
+ return OK;
+
+ if (tv1->vval.v_list == NULL)
+ {
+ tv1->vval.v_list = tv2->vval.v_list;
+ ++tv1->vval.v_list->lv_refcount;
+ }
+ else
+ list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
+
+ return OK;
+}
+
+/*
+ * Handle number operations:
+ * nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr
+ *
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_number(typval_T *tv1, typval_T *tv2, char_u *op)
{
varnumber_T n;
+ int failed = FALSE;
+
+ n = tv_get_number(tv1);
+ if (tv2->v_type == VAR_FLOAT)
+ {
+ float_T f = n;
+
+ if (*op == '%')
+ return FAIL;
+ switch (*op)
+ {
+ case '+': f += tv2->vval.v_float; break;
+ case '-': f -= tv2->vval.v_float; break;
+ case '*': f *= tv2->vval.v_float; break;
+ case '/': f /= tv2->vval.v_float; break;
+ }
+ clear_tv(tv1);
+ tv1->v_type = VAR_FLOAT;
+ tv1->vval.v_float = f;
+ }
+ else
+ {
+ switch (*op)
+ {
+ case '+': n += tv_get_number(tv2); break;
+ case '-': n -= tv_get_number(tv2); break;
+ case '*': n *= tv_get_number(tv2); break;
+ case '/': n = num_divide(n, tv_get_number(tv2), &failed); break;
+ case '%': n = num_modulus(n, tv_get_number(tv2), &failed); break;
+ }
+ clear_tv(tv1);
+ tv1->v_type = VAR_NUMBER;
+ tv1->vval.v_number = n;
+ }
+
+ return failed ? FAIL : OK;
+}
+
+/*
+ * Handle "str1 .= str2"
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_string(typval_T *tv1, typval_T *tv2, char_u *op UNUSED)
+{
char_u numbuf[NUMBUFLEN];
char_u *s;
- int failed = FALSE;
- // Can't do anything with a Funcref or Dict or Type on the right.
+ if (tv2->v_type == VAR_FLOAT)
+ return FAIL;
+
+ // str .= str
+ s = tv_get_string(tv1);
+ s = concat_str(s, tv_get_string_buf(tv2, numbuf));
+ clear_tv(tv1);
+ tv1->v_type = VAR_STRING;
+ tv1->vval.v_string = s;
+
+ return OK;
+}
+
+/*
+ * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
+ * and "tv1 .= tv2"
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_nr_or_string(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ if (tv2->v_type == VAR_LIST)
+ return FAIL;
+
+ if (vim_strchr((char_u *)"+-*/%", *op) != NULL)
+ return tv_op_number(tv1, tv2, op);
+
+ return tv_op_string(tv1, tv2, op);
+}
+
+/*
+ * Handle "f1 += f2", "f1 -= f2", "f1 *= f2", "f1 /= f2".
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_float(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ float_T f;
+
+ if (*op == '%' || *op == '.'
+ || (tv2->v_type != VAR_FLOAT
+ && tv2->v_type != VAR_NUMBER
+ && tv2->v_type != VAR_STRING))
+ return FAIL;
+
+ if (tv2->v_type == VAR_FLOAT)
+ f = tv2->vval.v_float;
+ else
+ f = tv_get_number(tv2);
+ switch (*op)
+ {
+ case '+': tv1->vval.v_float += f; break;
+ case '-': tv1->vval.v_float -= f; break;
+ case '*': tv1->vval.v_float *= f; break;
+ case '/': tv1->vval.v_float /= f; break;
+ }
+
+ return OK;
+}
+
+/*
+ * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
+ * and "tv1 .= tv2"
+ * Returns OK or FAIL.
+ */
+ int
+tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ // Can't do anything with a Funcref or Dict on the right.
// v:true and friends only work with "..=".
- if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
- && tv2->v_type != VAR_CLASS && tv2->v_type != VAR_TYPEALIAS
- && ((tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL)
- || *op == '.'))
- {
- switch (tv1->v_type)
- {
- case VAR_UNKNOWN:
- case VAR_ANY:
- case VAR_VOID:
- case VAR_DICT:
- case VAR_FUNC:
- case VAR_PARTIAL:
- case VAR_BOOL:
- case VAR_SPECIAL:
- case VAR_JOB:
- case VAR_CHANNEL:
- case VAR_INSTR:
- case VAR_OBJECT:
- break;
- case VAR_CLASS:
- case VAR_TYPEALIAS:
- check_typval_is_value(tv1);
- return FAIL;
+ if (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_DICT
+ || ((tv2->v_type == VAR_BOOL || tv2->v_type == VAR_SPECIAL)
+ && *op != '.'))
+ {
+ semsg(_(e_wrong_variable_type_for_str_equal), op);
+ return FAIL;
+ }
- case VAR_BLOB:
- if (*op != '+' || tv2->v_type != VAR_BLOB)
- break;
- // BLOB += BLOB
- if (tv1->vval.v_blob != NULL && tv2->vval.v_blob != NULL)
- {
- blob_T *b1 = tv1->vval.v_blob;
- blob_T *b2 = tv2->vval.v_blob;
- int i, len = blob_len(b2);
- for (i = 0; i < len; i++)
- ga_append(&b1->bv_ga, blob_get(b2, i));
- }
- return OK;
+ if (tv2->v_type == VAR_CLASS || tv2->v_type == VAR_TYPEALIAS)
+ {
+ check_typval_is_value(tv2);
+ return FAIL;
+ }
- case VAR_LIST:
- if (*op != '+' || tv2->v_type != VAR_LIST)
- break;
- // List += List
- if (tv2->vval.v_list != NULL)
- {
- if (tv1->vval.v_list == NULL)
- {
- tv1->vval.v_list = tv2->vval.v_list;
- ++tv1->vval.v_list->lv_refcount;
- }
- else
- list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
- }
- return OK;
+ int retval = FAIL;
+ switch (tv1->v_type)
+ {
+ case VAR_UNKNOWN:
+ case VAR_ANY:
+ case VAR_VOID:
+ case VAR_DICT:
+ case VAR_FUNC:
+ case VAR_PARTIAL:
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ case VAR_JOB:
+ case VAR_CHANNEL:
+ case VAR_INSTR:
+ case VAR_OBJECT:
+ break;
- case VAR_NUMBER:
- case VAR_STRING: