summaryrefslogtreecommitdiffstats
path: root/src/vim9compile.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 02:10:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 02:10:58 +0000
commitbee19c22d569e54513a9c591441c7f411811dc81 (patch)
treeb990d2df9fddb8194bfe49e9205005a0d952bc1f /src/vim9compile.c
parentAdding upstream version 2:9.1.0199. (diff)
downloadvim-bee19c22d569e54513a9c591441c7f411811dc81.tar.xz
vim-bee19c22d569e54513a9c591441c7f411811dc81.zip
Adding upstream version 2:9.1.0374.upstream/2%9.1.0374
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/vim9compile.c')
-rw-r--r--src/vim9compile.c758
1 files changed, 428 insertions, 330 deletions
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 8f67d8a..8e6f6e2 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1222,7 +1222,7 @@ compile_one_expr_in_str(char_u *p, cctx_T *cctx)
}
if (compile_expr0(&block_start, cctx) == FAIL)
return NULL;
- may_generate_2STRING(-1, TRUE, cctx);
+ may_generate_2STRING(-1, TOSTRING_INTERPOLATE, cctx);
return block_end + 1;
}
@@ -1367,6 +1367,7 @@ generate_loadvar(cctx_T *cctx, lhs_T *lhs)
generate_LOAD(cctx, ISN_LOADT, 0, name + 2, type);
break;
case dest_script:
+ case dest_script_v9:
res = compile_load_scriptvar(cctx,
name + (name[1] == ':' ? 2 : 0), NULL, NULL);
break;
@@ -1614,6 +1615,13 @@ lhs_class_member_modifiable(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
return FALSE;
}
+ if (IS_ENUM(cl))
+ {
+ semsg(_(e_enumvalue_str_cannot_be_modified), cl->class_name,
+ m->ocm_name);
+ return FALSE;
+ }
+
// If it is private member variable, then accessing it outside the
// class is not allowed.
// If it is a read only class variable, then it can be modified
@@ -1783,10 +1791,11 @@ compile_lhs(
return FAIL;
lhs->lhs_dest = dest_class_member;
- lhs->lhs_class = cctx->ctx_ufunc->uf_class;
- lhs->lhs_type =
- oc_member_type_by_idx(cctx->ctx_ufunc->uf_class,
- FALSE, lhs->lhs_classmember_idx);
+ // The class variable is defined either in the current class or
+ // in one of the parent class in the hierarchy.
+ lhs->lhs_class = defcl;
+ lhs->lhs_type = oc_member_type_by_idx(defcl, FALSE,
+ lhs->lhs_classmember_idx);
}
else
{
@@ -1830,7 +1839,8 @@ compile_lhs(
return FAIL;
}
- lhs->lhs_dest = dest_script;
+ lhs->lhs_dest = current_script_is_vim9()
+ ? dest_script_v9 : dest_script;
// existing script-local variables should have a type
lhs->lhs_scriptvar_sid = current_sctx.sc_sid;
@@ -2037,6 +2047,25 @@ compile_lhs(
return FAIL;
}
+ if (IS_ENUM(cl))
+ {
+ if (!inside_class(cctx, cl))
+ {
+ semsg(_(e_enumvalue_str_cannot_be_modified),
+ cl->class_name, m->ocm_name);
+ return FALSE;
+ }
+ if (lhs->lhs_type->tt_type == VAR_OBJECT &&
+ lhs->lhs_member_idx < 2)
+ {
+ char *msg = lhs->lhs_member_idx == 0 ?
+ e_enum_str_name_cannot_be_modified :
+ e_enum_str_ordinal_cannot_be_modified;
+ semsg(_(msg), cl->class_name);
+ return FALSE;
+ }
+ }
+
// If it is private member variable, then accessing it outside the
// class is not allowed.
// If it is a read only class variable, then it can be modified
@@ -2304,7 +2333,7 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
if (compile_load_lhs(lhs, var_start, lhs->lhs_type, cctx) == FAIL)
return FAIL;
}
- if (cl->class_flags & CLASS_INTERFACE)
+ if (IS_INTERFACE(cl))
return generate_GET_ITF_MEMBER(cctx, cl, lhs->lhs_member_idx, type);
return generate_GET_OBJ_MEMBER(cctx, lhs->lhs_member_idx, type);
}
@@ -2394,7 +2423,7 @@ compile_assign_unlet(
return FAIL;
}
if (dest_type == VAR_DICT
- && may_generate_2STRING(-1, FALSE, cctx) == FAIL)
+ && may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
return FAIL;
if (dest_type == VAR_LIST || dest_type == VAR_BLOB)
{
@@ -2453,7 +2482,7 @@ compile_assign_unlet(
{
class_T *cl = lhs->lhs_type->tt_class;
- if (cl->class_flags & CLASS_INTERFACE)
+ if (IS_INTERFACE(cl))
{
// "this.value": load "this" object and get the value
// at index for an object or class member get the type
@@ -2948,7 +2977,7 @@ compile_assignment(
if (*op == '.')
{
- if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
+ if (may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
goto theend;
}
else
@@ -2999,8 +3028,9 @@ compile_assignment(
else
{
if (is_decl && cmdidx == CMD_const && (lhs.lhs_dest == dest_script
- || lhs.lhs_dest == dest_global
- || lhs.lhs_dest == dest_local))
+ || lhs.lhs_dest == dest_script_v9
+ || lhs.lhs_dest == dest_global
+ || lhs.lhs_dest == dest_local))
// ":const var": lock the value, but not referenced variables
generate_LOCKCONST(cctx);
@@ -3244,254 +3274,185 @@ add_def_function(ufunc_T *ufunc)
}
/*
- * After ex_function() has collected all the function lines: parse and compile
- * the lines into instructions.
- * Adds the function to "def_functions".
- * When "check_return_type" is set then set ufunc->uf_ret_type to the type of
- * the return statement (used for lambda). When uf_ret_type is already set
- * then check that it matches.
- * When "profiling" is true add ISN_PROF_START instructions.
- * "outer_cctx" is set for a nested function.
- * This can be used recursively through compile_lambda(), which may reallocate
- * "def_functions".
- * Returns OK or FAIL.
+ * For an object constructor, generate instruction to setup "this" (the first
+ * local variable) and to initialize the object variables.
*/
- int
-compile_def_function(
- ufunc_T *ufunc,
- int check_return_type,
- compiletype_T compile_type,
- cctx_T *outer_cctx)
+ static int
+obj_constructor_prologue(ufunc_T *ufunc, cctx_T *cctx)
{
- char_u *line = NULL;
- garray_T lines_to_free;
- char_u *p;
- char *errormsg = NULL; // error message
- cctx_T cctx;
- garray_T *instr;
- int did_emsg_before = did_emsg;
- int did_emsg_silent_before = did_emsg_silent;
- int ret = FAIL;
- sctx_T save_current_sctx = current_sctx;
- int save_estack_compiling = estack_compiling;
- int save_cmod_flags = cmdmod.cmod_flags;
- int do_estack_push;
- int new_def_function = FALSE;
-#ifdef FEAT_PROFILE
- int prof_lnum = -1;
-#endif
- int debug_lnum = -1;
-
- // allocated lines are freed at the end
- ga_init2(&lines_to_free, sizeof(char_u *), 50);
+ generate_CONSTRUCT(cctx, ufunc->uf_class);
- // When using a function that was compiled before: Free old instructions.
- // The index is reused. Otherwise add a new entry in "def_functions".
- if (ufunc->uf_dfunc_idx > 0)
+ for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
{
- dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
- + ufunc->uf_dfunc_idx;
- isn_T *instr_dest = NULL;
+ ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
- switch (compile_type)
+ if (i < 2 && IS_ENUM(ufunc->uf_class))
+ // The first two object variables in an enum are the name
+ // and the ordinal. These are set by the ISN_CONSTRUCT
+ // instruction. So don't generate instructions to set
+ // these variables.
+ continue;
+
+ if (m->ocm_init != NULL)
{
- case CT_PROFILE:
-#ifdef FEAT_PROFILE
- instr_dest = dfunc->df_instr_prof; break;
-#endif
- case CT_NONE: instr_dest = dfunc->df_instr; break;
- case CT_DEBUG: instr_dest = dfunc->df_instr_debug; break;
+ char_u *expr = m->ocm_init;
+
+ if (compile_expr0(&expr, cctx) == FAIL)
+ return FAIL;
+
+ if (!ends_excmd2(m->ocm_init, expr))
+ {
+ semsg(_(e_trailing_characters_str), expr);
+ return FAIL;
+ }
+
+ type_T *type = get_type_on_stack(cctx, 0);
+ if (m->ocm_type->tt_type == VAR_ANY
+ && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
+ && type->tt_type != VAR_SPECIAL)
+ {
+ // If the member variable type is not yet set, then use
+ // the initialization expression type.
+ m->ocm_type = type;
+ }
+ else if (m->ocm_type->tt_type != type->tt_type)
+ {
+ // The type of the member initialization expression is
+ // determined at run time. Add a runtime type check.
+ where_T where = WHERE_INIT;
+ where.wt_kind = WT_MEMBER;
+ where.wt_func_name = (char *)m->ocm_name;
+ if (need_type_where(type, m->ocm_type, FALSE, -1,
+ where, cctx, FALSE, FALSE) == FAIL)
+ return FAIL;
+ }
}
- if (instr_dest != NULL)
- // Was compiled in this mode before: Free old instructions.
- delete_def_function_contents(dfunc, FALSE);
- ga_clear_strings(&dfunc->df_var_names);
- dfunc->df_defer_var_idx = 0;
- }
- else
- {
- if (add_def_function(ufunc) == FAIL)
- return FAIL;
- new_def_function = TRUE;
- }
+ else
+ push_default_value(cctx, m->ocm_type->tt_type, FALSE, NULL);
- if ((ufunc->uf_flags & FC_CLOSURE) && outer_cctx == NULL)
- {
- semsg(_(e_compiling_closure_without_context_str),
- printable_func_name(ufunc));
- return FAIL;
+ generate_STORE_THIS(cctx, i);
}
- ufunc->uf_def_status = UF_COMPILING;
-
- CLEAR_FIELD(cctx);
+ return OK;
+}
- cctx.ctx_compile_type = compile_type;
- cctx.ctx_ufunc = ufunc;
- cctx.ctx_lnum = -1;
- cctx.ctx_outer = outer_cctx;
- ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10);
- // Each entry on the type stack consists of two type pointers.
- ga_init2(&cctx.ctx_type_stack, sizeof(type2_T), 50);
- cctx.ctx_type_list = &ufunc->uf_type_list;
- ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50);
- instr = &cctx.ctx_instr;
+/*
+ * For an object method and an constructor, generate instruction to setup
+ * "this" (the first local variable). For a constructor, generate instructions
+ * to initialize the object variables.
+ */
+ static int
+obj_method_prologue(ufunc_T *ufunc, cctx_T *cctx)
+{
+ dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
- // Set the context to the function, it may be compiled when called from
- // another script. Set the script version to the most modern one.
- // The line number will be set in next_line_from_context().
- current_sctx = ufunc->uf_script_ctx;
- current_sctx.sc_version = SCRIPT_VERSION_VIM9;
+ if (GA_GROW_FAILS(&dfunc->df_var_names, 1))
+ return FAIL;
- // Don't use the flag from ":legacy" here.
- cmdmod.cmod_flags &= ~CMOD_LEGACY;
+ ((char_u **)dfunc->df_var_names.ga_data)[0] =
+ vim_strsave((char_u *)"this");
+ ++dfunc->df_var_names.ga_len;
- // Make sure error messages are OK.
- do_estack_push = !estack_top_is_ufunc(ufunc, 1);
- if (do_estack_push)
- estack_push_ufunc(ufunc, 1);
- estack_compiling = TRUE;
+ // In the constructor allocate memory for the object and initialize the
+ // object members.
+ if (IS_CONSTRUCTOR_METHOD(ufunc))
+ return obj_constructor_prologue(ufunc, cctx);
- if (check_args_shadowing(ufunc, &cctx) == FAIL)
- goto erret;
+ return OK;
+}
- // For an object method and constructor "this" is the first local variable.
- if (ufunc->uf_flags & (FC_OBJECT|FC_NEW))
+/*
+ * Produce instructions for the default values of optional arguments.
+ */
+ static int
+compile_def_function_default_args(
+ ufunc_T *ufunc,
+ cctx_T *cctx,
+ garray_T *instr)
+{
+ int count = ufunc->uf_def_args.ga_len;
+ int first_def_arg = ufunc->uf_args.ga_len - count;
+ int i;
+ int off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0);
+ int did_set_arg_type = FALSE;
+
+ // Produce instructions for the default values of optional arguments.
+ SOURCING_LNUM = 0; // line number unknown
+ for (i = 0; i < count; ++i)
{
- dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
- + ufunc->uf_dfunc_idx;
- if (GA_GROW_FAILS(&dfunc->df_var_names, 1))
- goto erret;
- ((char_u **)dfunc->df_var_names.ga_data)[0] =
- vim_strsave((char_u *)"this");
- ++dfunc->df_var_names.ga_len;
+ char_u *arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
+ if (STRCMP(arg, "v:none") == 0)
+ // "arg = v:none" means the argument is optional without
+ // setting a value when the argument is missing.
+ continue;
- // In the constructor allocate memory for the object and initialize the
- // object members.
- if (IS_CONSTRUCTOR_METHOD(ufunc))
- {
- generate_CONSTRUCT(&cctx, ufunc->uf_class);
+ type_T *val_type;
+ int arg_idx = first_def_arg + i;
+ where_T where = WHERE_INIT;
+ int jump_instr_idx = instr->ga_len;
+ isn_T *isn;
- for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
- {
- ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
- if (m->ocm_init != NULL)
- {
- char_u *expr = m->ocm_init;
- if (compile_expr0(&expr, &cctx) == FAIL)
- goto erret;
- if (!ends_excmd2(m->ocm_init, expr))
- {
- semsg(_(e_trailing_characters_str), expr);
- goto erret;
- }
+ // Use a JUMP_IF_ARG_SET instruction to skip if the value was given.
+ if (generate_JUMP_IF_ARG(cctx, ISN_JUMP_IF_ARG_SET,
+ i - count - off) == FAIL)
+ return FAIL;
- type_T *type = get_type_on_stack(&cctx, 0);
- if (m->ocm_type->tt_type == VAR_ANY
- && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
- && type->tt_type != VAR_SPECIAL)
- {
- // If the member variable type is not yet set, then use
- // the initialization expression type.
- m->ocm_type = type;
- }
- else if (m->ocm_type->tt_type != type->tt_type)
- {
- // The type of the member initialization expression is
- // determined at run time. Add a runtime type check.
- where_T where = WHERE_INIT;
- where.wt_kind = WT_MEMBER;
- where.wt_func_name = (char *)m->ocm_name;
- if (need_type_where(type, m->ocm_type, FALSE, -1,
- where, &cctx, FALSE, FALSE) == FAIL)
- goto erret;
- }
- }
- else
- push_default_value(&cctx, m->ocm_type->tt_type,
- FALSE, NULL);
- generate_STORE_THIS(&cctx, i);
- }
- }
- }
+ // Make sure later arguments are not found.
+ ufunc->uf_args_visible = arg_idx;
- if (ufunc->uf_def_args.ga_len > 0)
- {
- int count = ufunc->uf_def_args.ga_len;
- int first_def_arg = ufunc->uf_args.ga_len - count;
- int i;
- int off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0);
- int did_set_arg_type = FALSE;
-
- // Produce instructions for the default values of optional arguments.
- SOURCING_LNUM = 0; // line number unknown
- for (i = 0; i < count; ++i)
+ int r = compile_expr0(&arg, cctx);
+ if (r == FAIL)
+ return FAIL;
+
+ // If no type specified use the type of the default value.
+ // Otherwise check that the default value type matches the
+ // specified type.
+ val_type = get_type_on_stack(cctx, 0);
+ where.wt_index = arg_idx + 1;
+ where.wt_kind = WT_ARGUMENT;
+ if (ufunc->uf_arg_types[arg_idx] == &t_unknown)
{
- char_u *arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
- if (STRCMP(arg, "v:none") == 0)
- // "arg = v:none" means the argument is optional without
- // setting a value when the argument is missing.
- continue;
+ did_set_arg_type = TRUE;
+ ufunc->uf_arg_types[arg_idx] = val_type;
+ }
+ else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx],
+ FALSE, -1, where, cctx, FALSE, FALSE) == FAIL)
+ return FAIL;
- type_T *val_type;
- int arg_idx = first_def_arg + i;
- where_T where = WHERE_INIT;
- int jump_instr_idx = instr->ga_len;
- isn_T *isn;
-
- // Use a JUMP_IF_ARG_SET instruction to skip if the value was given.
- if (generate_JUMP_IF_ARG(&cctx, ISN_JUMP_IF_ARG_SET,
- i - count - off) == FAIL)
- goto erret;
-
- // Make sure later arguments are not found.
- ufunc->uf_args_visible = arg_idx;
-
- int r = compile_expr0(&arg, &cctx);
- if (r == FAIL)
- goto erret;
-
- // If no type specified use the type of the default value.
- // Otherwise check that the default value type matches the
- // specified type.
- val_type = get_type_on_stack(&cctx, 0);
- where.wt_index = arg_idx + 1;
- where.wt_kind = WT_ARGUMENT;
- if (ufunc->uf_arg_types[arg_idx] == &t_unknown)
- {
- did_set_arg_type = TRUE;
- ufunc->uf_arg_types[arg_idx] = val_type;
- }
- else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx],
- FALSE, -1, where, &cctx, FALSE, FALSE) == FAIL)
- goto erret;
+ if (generate_STORE(cctx, ISN_STORE, i - count - off, NULL) == FAIL)
+ return FAIL;
- if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
- goto erret;
+ // set instruction index in JUMP_IF_ARG_SET to here
+ isn = ((isn_T *)instr->ga_data) + jump_instr_idx;
+ isn->isn_arg.jumparg.jump_where = instr->ga_len;
+ }
- // set instruction index in JUMP_IF_ARG_SET to here
- isn = ((isn_T *)instr->ga_data) + jump_instr_idx;
- isn->isn_arg.jumparg.jump_where = instr->ga_len;
- }
+ if (did_set_arg_type)
+ set_function_type(ufunc);
- if (did_set_arg_type)
- set_function_type(ufunc);
- }
- ufunc->uf_args_visible = ufunc->uf_args.ga_len;
+ return OK;
+}
- // Compiling a function in an interface is done to get the function type.
- // No code is actually compiled.
- if (ufunc->uf_class != NULL
- && (ufunc->uf_class->class_flags & CLASS_INTERFACE))
- {
- ufunc->uf_def_status = UF_NOT_COMPILED;
- ret = OK;
- goto erret;
- }
+/*
+ * Compile def function body. Loop over all the lines in the function and
+ * generate instructions.
+ */
+ static int
+compile_def_function_body(
+ cctx_T *cctx,
+ int last_func_lnum,
+ int check_return_type,
+ garray_T *lines_to_free,
+ char **errormsg)
+{
+ char_u *line = NULL;
+ char_u *p;
+ int did_emsg_before = did_emsg;
+#ifdef FEAT_PROFILE
+ int prof_lnum = -1;
+#endif
+ int debug_lnum = -1;
- /*
- * Loop over all the lines of the function and generate instructions.
- */
for (;;)
{
exarg_T ea;
@@ -3502,29 +3463,29 @@ compile_def_function(
// Bail out on the first error to avoid a flood of errors and report
// the right line number when inside try/catch.
if (did_emsg_before != did_emsg)
- goto erret;
+ return FAIL;
if (line != NULL && *line == '|')
// the line continues after a '|'
++line;
else if (line != NULL && *skipwhite(line) != NUL
- && !(*line == '#' && (line == cctx.ctx_line_start
+ && !(*line == '#' && (line == cctx->ctx_line_start
|| VIM_ISWHITE(line[-1]))))
{
semsg(_(e_trailing_characters_str), line);
- goto erret;
+ return FAIL;
}
else if (line != NULL && vim9_bad_comment(skipwhite(line)))
- goto erret;
+ return FAIL;
else
{
- line = next_line_from_context(&cctx, FALSE);
- if (cctx.ctx_lnum >= ufunc->uf_lines.ga_len)
+ line = next_line_from_context(cctx, FALSE);
+ if (cctx->ctx_lnum >= last_func_lnum)
{
// beyond the last line
#ifdef FEAT_PROFILE
- if (cctx.ctx_skip != SKIP_YES)
- may_generate_prof_end(&cctx, prof_lnum);
+ if (cctx->ctx_skip != SKIP_YES)
+ may_generate_prof_end(cctx, prof_lnum);
#endif
break;
}
@@ -3533,42 +3494,42 @@ compile_def_function(
if (line != NULL)
{
line = vim_strsave(line);
- if (ga_add_string(&lines_to_free, line) == FAIL)
- goto erret;
+ if (ga_add_string(lines_to_free, line) == FAIL)
+ return FAIL;
}
}
CLEAR_FIELD(ea);
ea.cmdlinep = &line;
ea.cmd = skipwhite(line);
- ea.skip = cctx.ctx_skip == SKIP_YES;
+ ea.skip = cctx->ctx_skip == SKIP_YES;
if (*ea.cmd == '#')
{
// "#" starts a comment, but "#{" is an error
if (vim9_bad_comment(ea.cmd))
- goto erret;
+ return FAIL;
line = (char_u *)"";
continue;
}
#ifdef FEAT_PROFILE
- if (cctx.ctx_compile_type == CT_PROFILE && cctx.ctx_lnum != prof_lnum
- && cctx.ctx_skip != SKIP_YES)
+ if (cctx->ctx_compile_type == CT_PROFILE && cctx->ctx_lnum != prof_lnum
+ && cctx->ctx_skip != SKIP_YES)
{
- may_generate_prof_end(&cctx, prof_lnum);
+ may_generate_prof_end(cctx, prof_lnum);
- prof_lnum = cctx.ctx_lnum;
- generate_instr(&cctx, ISN_PROF_START);
+ prof_lnum = cctx->ctx_lnum;
+ generate_instr(cctx, ISN_PROF_START);
}
#endif
- if (cctx.ctx_compile_type == CT_DEBUG && cctx.ctx_lnum != debug_lnum
- && cctx.ctx_skip != SKIP_YES)
+ if (cctx->ctx_compile_type == CT_DEBUG && cctx->ctx_lnum != debug_lnum
+ && cctx->ctx_skip != SKIP_YES)
{
- debug_lnum = cctx.ctx_lnum;
- generate_instr_debug(&cctx);
+ debug_lnum = cctx->ctx_lnum;
+ generate_instr_debug(cctx);
}
- cctx.ctx_prev_lnum = cctx.ctx_lnum + 1;
+ cctx->ctx_prev_lnum = cctx->ctx_lnum + 1;
// Some things can be recognized by the first character.
switch (*ea.cmd)
@@ -3576,18 +3537,18 @@ compile_def_function(
case '}':
{
// "}" ends a block scope
- scopetype_T stype = cctx.ctx_scope == NULL
- ? NO_SCOPE : cctx.ctx_scope->se_type;
+ scopetype_T stype = cctx->ctx_scope == NULL
+ ? NO_SCOPE : cctx->ctx_scope->se_type;
if (stype == BLOCK_SCOPE)
{
- compile_endblock(&cctx);
+ compile_endblock(cctx);
line = ea.cmd;
}
else
{
emsg(_(e_using_rcurly_outside_if_block_scope));
- goto erret;
+ return FAIL;
}
if (line != NULL)
line = skipwhite(ea.cmd + 1);
@@ -3599,7 +3560,7 @@ compile_def_function(
// "{'a': 1}->func() is something else
if (ends_excmd(*skipwhite(ea.cmd + 1)))
{
- line = compile_block(ea.cmd, &cctx);
+ line = compile_block(ea.cmd, cctx);
continue;
}
break;
@@ -3608,11 +3569,11 @@ compile_def_function(
/*
* COMMAND MODIFIERS
*/
- cctx.ctx_has_cmdmod = FALSE;
- if (parse_command_modifiers(&ea, &errormsg, &local_cmdmod, FALSE)
+ cctx->ctx_has_cmdmod = FALSE;
+ if (parse_command_modifiers(&ea, errormsg, &local_cmdmod, FALSE)
== FAIL)
- goto erret;
- generate_cmdmods(&cctx, &local_cmdmod);
+ return FAIL;
+ generate_cmdmods(cctx, &local_cmdmod);
undo_cmdmod(&local_cmdmod);
// Check if there was a colon after the last command modifier or before
@@ -3643,11 +3604,11 @@ compile_def_function(
int assign;
// Check for assignment after command modifiers.
- assign = may_compile_assignment(&ea, &line, &cctx);
+ assign = may_compile_assignment(&ea, &line, cctx);
if (assign == OK)
goto nextline;
if (assign == FAIL)
- goto erret;
+ return FAIL;
}
}
@@ -3675,13 +3636,13 @@ compile_def_function(
&& !(local_cmdmod.cmod_flags & CMOD_LEGACY))
{
semsg(_(e_colon_required_before_range_str), cmd);
- goto erret;
+ return FAIL;
}
ea.addr_count = 1;
if (ends_excmd2(line, ea.cmd))
{
// A range without a command: jump to the line.
- generate_EXEC(&cctx, ISN_EXECRANGE,
+ generate_EXEC(cctx, ISN_EXECRANGE,
vim_strnsave(cmd, ea.cmd - cmd));
line = ea.cmd;
goto nextline;
@@ -3690,13 +3651,13 @@ compile_def_function(
}
p = find_ex_command(&ea, NULL,
starts_with_colon || (local_cmdmod.cmod_flags & CMOD_LEGACY)
- ? NULL : item_exists, &cctx);
+ ? NULL : item_exists, cctx);
if (p == NULL)
{
- if (cctx.ctx_skip != SKIP_YES)
+ if (cctx->ctx_skip != SKIP_YES)
semsg(_(e_ambiguous_use_of_user_defined_command_str), ea.cmd);
- goto erret;
+ return FAIL;
}
// When using ":legacy cmd" always use compile_exec().
@@ -3721,7 +3682,7 @@ compile_def_function(
case CMD_finally:
case CMD_endtry:
semsg(_(e_cannot_use_legacy_with_command_str), ea.cmd);
- goto erret;
+ return FAIL;
default: break;
}
@@ -3738,7 +3699,7 @@ compile_def_function(
// "p" is equal to "ea.cmd" for a valid command.
if (ea.cmdidx == CMD_eval || ea.cmdidx == CMD_var)
;
- else if (cctx.ctx_skip == SKIP_YES)
+ else if (cctx->ctx_skip == SKIP_YES)
{
line += STRLEN(line);
goto nextline;
@@ -3746,11 +3707,11 @@ compile_def_function(
else
{
semsg(_(e_command_not_recognized_str), ea.cmd);
- goto erret;
+ return FAIL;
}
}
- if ((cctx.ctx_had_return || cctx.ctx_had_throw)
+ if ((cctx->ctx_had_return || cctx->ctx_had_throw)
&& ea.cmdidx != CMD_elseif
&& ea.cmdidx != CMD_else
&& ea.cmdidx != CMD_endif
@@ -3762,10 +3723,10 @@ compile_def_function(
&& !ignore_unreachable_code_for_testing)
{
semsg(_(e_unreachable_code_after_str),
- cctx.ctx_had_return ? "return" : "throw");
- goto erret;
+ cctx->ctx_had_return ? "return" : "throw");
+ return FAIL;
}
- cctx.ctx_had_throw = FALSE;
+ cctx->ctx_had_throw = FALSE;
p = skipwhite(p);
if (ea.cmdidx != CMD_SIZE
@@ -3781,7 +3742,7 @@ compile_def_function(
if ((ea.argt & EX_RANGE) == 0 && ea.addr_count > 0)
{
emsg(_(e_no_range_allowed));
- goto erret;
+ return FAIL;
}
}
@@ -3790,13 +3751,13 @@ compile_def_function(
case CMD_def:
case CMD_function:
ea.arg = p;
- line = compile_nested_function(&ea, &cctx, &lines_to_free);
+ line = compile_nested_function(&ea, cctx, lines_to_free);
break;
case CMD_return:
line = compile_return(p, check_return_type,
- local_cmdmod.cmod_flags & CMOD_LEGACY, &cctx);
- cctx.ctx_had_return = TRUE;
+ local_cmdmod.cmod_flags & CMOD_LEGACY, cctx);
+ cctx->ctx_had_return = TRUE;
break;
case CMD_let:
@@ -3807,7 +3768,7 @@ compile_def_function(
case CMD_const:
case CMD_increment:
case CMD_decrement:
- line = compile_assignment(p, &ea, ea.cmdidx, &cctx);
+ line = compile_assignment(p, &ea, ea.cmdidx, cctx);
if (line == p)
{
emsg(_(e_invalid_assignment));
@@ -3818,7 +3779,7 @@ compile_def_function(
case CMD_unlet:
case CMD_unlockvar:
case CMD_lockvar:
- line = compile_unletlock(p, &ea, &cctx);
+ line = compile_unletlock(p, &ea, cctx);
break;
case CMD_import:
@@ -3827,67 +3788,67 @@ compile_def_function(
break;
case CMD_if:
- line = compile_if(p, &cctx);
+ line = compile_if(p, cctx);
break;
case CMD_elseif:
- line = compile_elseif(p, &cctx);
- cctx.ctx_had_return = FALSE;
+ line = compile_elseif(p, cctx);
+ cctx->ctx_had_return = FALSE;
break;
case CMD_else:
- line = compile_else(p, &cctx);
- cctx.ctx_had_return = FALSE;
+ line = compile_else(p, cctx);
+ cctx->ctx_had_return = FALSE;
break;
case CMD_endif:
- line = compile_endif(p, &cctx);
+ line = compile_endif(p, cctx);
break;
case CMD_while:
- line = compile_while(p, &cctx);
+ line = compile_while(p, cctx);
break;
case CMD_endwhile:
- line = compile_endwhile(p, &cctx);
- cctx.ctx_had_return = FALSE;
+ line = compile_endwhile(p, cctx);
+ cctx->ctx_had_return = FALSE;
break;
case CMD_for:
- line = compile_for(p, &cctx);
+ line = compile_for(p, cctx);
break;
case CMD_endfor:
- line = compile_endfor(p, &cctx);
- cctx.ctx_had_return = FALSE;
+ line = compile_endfor(p, cctx);
+ cctx->ctx_had_return = FALSE;
break;
case CMD_continue:
- line = compile_continue(p, &cctx);
+ line = compile_continue(p, cctx);
break;
case CMD_break:
- line = compile_break(p, &cctx);
+ line = compile_break(p, cctx);
break;
case CMD_try:
- line = compile_try(p, &cctx);
+ line = compile_try(p, cctx);
break;
case CMD_catch:
- line = compile_catch(p, &cctx);
- cctx.ctx_had_return = FALSE;
+ line = compile_catch(p, cctx);
+ cctx->ctx_had_return = FALSE;
break;
case CMD_finally:
- line = compile_finally(p, &cctx);
- cctx.ctx_had_return = FALSE;
+ line = compile_finally(p, cctx);
+ cctx->ctx_had_return = FALSE;
break;
case CMD_endtry:
- line = compile_endtry(p, &cctx);
+ line = compile_endtry(p, cctx);
break;
case CMD_throw:
- line = compile_throw(p, &cctx);
- cctx.ctx_had_throw = TRUE;
+ line = compile_throw(p, cctx);
+ cctx->ctx_had_throw = TRUE;
break;
case CMD_eval:
- line = compile_eval(p, &cctx);
+ line = compile_eval(p, cctx);
break;
case CMD_defer:
- line = compile_defer(p, &cctx);
+ line = compile_defer(p, cctx);
break;
#ifdef HAS_MESSAGE_WINDOW
@@ -3898,7 +3859,7 @@ compile_def_function(
line = NULL;
else
line = compile_mult_expr(p, ea.cmdidx,
- cmd_count, &cctx);
+ cmd_count, cctx);
}
break;
#endif
@@ -3908,29 +3869,29 @@ compile_def_function(
case CMD_echoerr:
case CMD_echomsg:
case CMD_execute:
- line = compile_mult_expr(p, ea.cmdidx, 0, &cctx);
+ line = compile_mult_expr(p, ea.cmdidx, 0, cctx);
break;
case CMD_put:
ea.cmd = cmd;
- line = compile_put(p, &ea, &cctx);
+ line = compile_put(p, &ea, cctx);
break;
case CMD_substitute:
if (check_global_and_subst(ea.cmd, p) == FAIL)
- goto erret;
- if (cctx.ctx_skip == SKIP_YES)
+ return FAIL;
+ if (cctx->ctx_skip == SKIP_YES)
line = (char_u *)"";
else
{
ea.arg = p;
- line = compile_substitute(line, &ea, &cctx);
+ line = compile_substitute(line, &ea, cctx);
}
break;
case CMD_redir:
ea.arg = p;
- line = compile_redir(line, &ea, &cctx);
+ line = compile_redir(line, &ea, cctx);
break;
case CMD_cexpr:
@@ -3941,7 +3902,7 @@ compile_def_function(
case CMD_lgetexpr:
#ifdef FEAT_QUICKFIX
ea.arg = p;
- line = compile_cexpr(line, &ea, &cctx);
+ line = compile_cexpr(line, &ea, cctx);
#else
ex_ni(&ea);
line = NULL;
@@ -3955,13 +3916,13 @@ compile_def_function(
case CMD_t:
case CMD_xit:
not_in_vim9(&ea);
- goto erret;
+ return FAIL;
case CMD_SIZE:
- if (cctx.ctx_skip != SKIP_YES)
+ if (cctx->ctx_skip != SKIP_YES)
{
semsg(_(e_invalid_command_str), ea.cmd);
- goto erret;
+ return FAIL;
}
// We don't check for a next command here.
line = (char_u *)"";
@@ -3978,52 +3939,189 @@ compile_def_function(
case CMD_tcl:
ea.arg = p;
if (vim_strchr(line, '\n') == NULL)
- line = compile_exec(line, &ea, &cctx);
+ line = compile_exec(line, &ea, cctx);
else
// heredoc lines have been concatenated with NL
// characters in get_function_body()
- line = compile_script(line, &cctx);
+ line = compile_script(line, cctx);
break;
case CMD_vim9script:
- if (cctx.ctx_skip != SKIP_YES)
+ if (cctx->ctx_skip != SKIP_YES)
{
emsg(_(e_vim9script_can_only_be_used_in_script));
- goto erret;
+ return FAIL;
}
line = (char_u *)"";
break;
+ case CMD_class:
+ emsg(_(e_class_can_only_be_used_in_script));
+ return FAIL;
+
case CMD_type:
emsg(_(e_type_can_only_be_used_in_script));
- goto erret;
- break;
+ return FAIL;
case CMD_global:
if (check_global_and_subst(ea.cmd, p) == FAIL)
- goto erret;
+ return FAIL;
// FALLTHROUGH
default:
// Not recognized, execute with do_cmdline_cmd().
ea.arg = p;
- line = compile_exec(line, &ea, &cctx);
+ line = compile_exec(line, &ea, cctx);
break;
}
nextline:
if (line == NULL)
- goto erret;
+ return FAIL;
line = skipwhite(line);
// Undo any command modifiers.
- generate_undo_cmdmods(&cctx);
+ generate_undo_cmdmods(cctx);
- if (cctx.ctx_type_stack.ga_len < 0)
+ if (cctx->ctx_type_stack.ga_len < 0)
{
iemsg("Type stack underflow");
- goto erret;
+ return FAIL;
}
} // END of the loop over all the function body lines.
+ return OK;
+}
+
+/*
+ * After ex_function() has collected all the function lines: parse and compile
+ * the lines into instructions.
+ * Adds the function to "def_functions".
+ * When "check_return_type" is set then set ufunc->uf_ret_type to the type of
+ * the return statement (used for lambda). When uf_ret_type is already set
+ * then check that it matches.
+ * When "profiling" is true add ISN_PROF_START instructions.
+ * "outer_cctx" is set for a nested function.
+ * This can be used recursively through compile_lambda(), which may reallocate
+ * "def_functions".
+ * Returns OK or FAIL.
+ */
+ int
+compile_def_function(
+ ufunc_T *ufunc,
+ int check_return_type,
+ compiletype_T compile_type,
+ cctx_T *outer_cctx)
+{
+ garray_T lines_to_free;
+ char *errormsg = NULL; // error message
+ cctx_T cctx;
+ garray_T *instr;
+ int did_emsg_before = did_emsg;
+ int did_emsg_silent_before = did_emsg_silent;
+ int ret = FAIL;
+ sctx_T save_current_sctx = current_sctx;
+ int save_estack_compiling = estack_compiling;
+ int save_cmod_flags = cmdmod.cmod_flags;
+ int do_estack_push;
+ int new_def_function = FALSE;
+
+ // allocated lines are freed at the end
+ ga_init2(&lines_to_free, sizeof(char_u *), 50);
+
+ // When using a function that was compiled before: Free old instructions.
+ // The index is reused. Otherwise add a new entry in "def_functions".
+ if (ufunc->uf_dfunc_idx > 0)
+ {
+ dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ + ufunc->uf_dfunc_idx;
+ isn_T *instr_dest = NULL;
+
+ switch (compile_type)
+ {
+ case CT_PROFILE:
+#ifdef FEAT_PROFILE
+ instr_dest = dfunc->df_instr_prof; break;
+#endif
+ case CT_NONE: instr_dest = dfunc->df_instr; break;
+ case CT_DEBUG: instr_dest = dfunc->df_instr_debug; break;
+ }
+ if (instr_dest != NULL)
+ // Was compiled in this mode before: Free old instructions.
+ delete_def_function_contents(dfunc, FALSE);
+ ga_clear_strings(&dfunc->df_var_names);
+ dfunc->df_defer_var_idx = 0;
+ }
+ else
+ {
+ if (add_def_function(ufunc) == FAIL)
+ return FAIL;
+ new_def_function = TRUE;
+ }
+
+ if ((ufunc->uf_flags & FC_CLOSURE) && outer_cctx == NULL)
+ {
+ semsg(_(e_compiling_closure_without_context_str),
+ printable_func_name(ufunc));
+ return FAIL;
+ }
+
+ ufunc->uf_def_status = UF_COMPILING;
+
+ CLEAR_FIELD(cctx);
+
+ cctx.ctx_compile_type = compile_type;
+ cctx.ctx_ufunc = ufunc;
+ cctx.ctx_lnum = -1;
+ cctx.ctx_outer = outer_cctx;
+ ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10);
+ // Each entry on the type stack consists of two type pointers.
+ ga_init2(&cctx.ctx_type_stack, sizeof(type2_T), 50);
+ cctx.ctx_type_list = &ufunc->uf_type_list;
+ ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50);
+ instr = &cctx.ctx_instr;
+
+ // Set the context to the function, it may be compiled when called from
+ // another script. Set the script version to the most modern one.
+ // The line number will be set in next_line_from_context().
+ current_sctx = ufunc->uf_script_ctx;
+ current_sctx.sc_version = SCRIPT_VERSION_VIM9;
+
+ // Don't use the flag from ":legacy" here.
+ cmdmod.cmod_flags &= ~CMOD_LEGACY;
+
+ // Make sure error messages are OK.
+ do_estack_push = !estack_top_is_ufunc(ufunc, 1);
+ if (do_estack_push)
+ estack_push_ufunc(ufunc, 1);
+ estack_compiling = TRUE;
+
+ if (check_args_shadowing(ufunc, &cctx) == FAIL)
+ goto erret;
+
+ // For an object method and a constructor generate instructions to
+ // initialize "this" and the object variables.
+ if (ufunc->uf_flags & (FC_OBJECT|FC_NEW))
+ if (obj_method_prologue(ufunc, &cctx) == FAIL)
+ goto erret;
+
+ if (ufunc->uf_def_args.ga_len > 0)
+ if (compile_def_function_default_args(ufunc, &cctx, instr) == FAIL)
+ goto erret;
+ ufunc->uf_args_visible = ufunc->uf_args.ga_len;
+
+ // Compiling a function in an interface is done to get the function type.
+ // No code is actually compiled.
+ if (ufunc->uf_class != NULL && IS_INTERFACE(ufunc->uf_class))
+ {
+ ufunc->uf_def_status = UF_NOT_COMPILED;
+ ret = OK;
+ goto erret;
+ }
+
+ // compile the function body
+ if (compile_def_function_body(&cctx, ufunc->uf_lines.ga_len,
+ check_return_type, &lines_to_free, &errormsg) == FAIL)
+ goto erret;
+
if (cctx.ctx_scope != NULL)
{
if (cctx.ctx_scope->se_type == IF_SCOPE)