diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 02:10:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 02:10:58 +0000 |
commit | bee19c22d569e54513a9c591441c7f411811dc81 (patch) | |
tree | b990d2df9fddb8194bfe49e9205005a0d952bc1f /src/vim9compile.c | |
parent | Adding upstream version 2:9.1.0199. (diff) | |
download | vim-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.c | 758 |
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) |