1 /* vi:set ts=8 sts=4 sw=4 noet: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 * See README.txt for an overview of the Vim source code. 8 */ 9 10 /* 11 * vim9script.c: :vim9script, :import, :export and friends 12 */ 13 14 #include "vim.h" 15 16 #if defined(FEAT_EVAL) 17 # include "vim9.h" 18 #endif 19 20 /* 21 * Return TRUE when currently using Vim9 script syntax. 22 * Does not go up the stack, a ":function" inside vim9script uses legacy 23 * syntax. 24 */ 25 int 26 in_vim9script(void) 27 { 28 // "sc_version" is also set when compiling a ":def" function in legacy 29 // script. 30 return current_sctx.sc_version == SCRIPT_VERSION_VIM9 31 || (cmdmod.cmod_flags & CMOD_VIM9CMD); 32 } 33 34 /* 35 * Return TRUE if the current script is Vim9 script. 36 * This also returns TRUE in a legacy function in a Vim9 script. 37 */ 38 int 39 current_script_is_vim9(void) 40 { 41 return SCRIPT_ID_VALID(current_sctx.sc_sid) 42 && SCRIPT_ITEM(current_sctx.sc_sid)->sn_version 43 == SCRIPT_VERSION_VIM9; 44 } 45 46 /* 47 * ":vim9script". 48 */ 49 void 50 ex_vim9script(exarg_T *eap UNUSED) 51 { 52 #ifdef FEAT_EVAL 53 int sid = current_sctx.sc_sid; 54 scriptitem_T *si; 55 56 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) 57 { 58 emsg(_(e_vim9script_can_only_be_used_in_script)); 59 return; 60 } 61 62 si = SCRIPT_ITEM(sid); 63 if (si->sn_state == SN_STATE_HAD_COMMAND) 64 { 65 emsg(_(e_vim9script_must_be_first_command_in_script)); 66 return; 67 } 68 if (!IS_WHITE_OR_NUL(*eap->arg) && STRCMP(eap->arg, "noclear") != 0) 69 { 70 semsg(_(e_invarg2), eap->arg); 71 return; 72 } 73 if (si->sn_state == SN_STATE_RELOAD && IS_WHITE_OR_NUL(*eap->arg)) 74 { 75 hashtab_T *ht = &SCRIPT_VARS(sid); 76 77 // Reloading a script without the "noclear" argument: clear 78 // script-local variables and functions. 79 hashtab_free_contents(ht); 80 hash_init(ht); 81 delete_script_functions(sid); 82 83 // old imports and script variables are no longer valid 84 free_imports_and_script_vars(sid); 85 } 86 si->sn_state = SN_STATE_HAD_COMMAND; 87 88 current_sctx.sc_version = SCRIPT_VERSION_VIM9; 89 si->sn_version = SCRIPT_VERSION_VIM9; 90 91 if (STRCMP(p_cpo, CPO_VIM) != 0) 92 { 93 si->sn_save_cpo = vim_strsave(p_cpo); 94 set_option_value((char_u *)"cpo", 0L, (char_u *)CPO_VIM, OPT_NO_REDRAW); 95 } 96 #else 97 // No check for this being the first command, it doesn't matter. 98 current_sctx.sc_version = SCRIPT_VERSION_VIM9; 99 #endif 100 } 101 102 /* 103 * When in Vim9 script give an error and return FAIL. 104 */ 105 int 106 not_in_vim9(exarg_T *eap) 107 { 108 if (in_vim9script()) 109 switch (eap->cmdidx) 110 { 111 case CMD_k: 112 if (eap->addr_count > 0) 113 { 114 emsg(_(e_norange)); 115 return FAIL; 116 } 117 // FALLTHROUGH 118 case CMD_append: 119 case CMD_change: 120 case CMD_insert: 121 case CMD_open: 122 case CMD_t: 123 case CMD_xit: 124 semsg(_(e_command_not_supported_in_vim9_script_missing_var_str), eap->cmd); 125 return FAIL; 126 default: break; 127 } 128 return OK; 129 } 130 131 /* 132 * Give an error message if "p" points at "#{" and return TRUE. 133 * This avoids that using a legacy style #{} dictionary leads to difficult to 134 * understand errors. 135 */ 136 int 137 vim9_bad_comment(char_u *p) 138 { 139 if (p[0] == '#' && p[1] == '{' && p[2] != '{') 140 { 141 emsg(_(e_cannot_use_hash_curly_to_start_comment)); 142 return TRUE; 143 } 144 return FALSE; 145 } 146 147 /* 148 * Return TRUE if "p" points at a "#" not followed by one '{'. 149 * Does not check for white space. 150 */ 151 int 152 vim9_comment_start(char_u *p) 153 { 154 return p[0] == '#' && (p[1] != '{' || p[2] == '{'); 155 } 156 157 #if defined(FEAT_EVAL) || defined(PROTO) 158 159 /* 160 * ":export let Name: type" 161 * ":export const Name: type" 162 * ":export def Name(..." 163 * ":export class Name ..." 164 */ 165 void 166 ex_export(exarg_T *eap) 167 { 168 if (!in_vim9script()) 169 { 170 emsg(_(e_export_can_only_be_used_in_vim9script)); 171 return; 172 } 173 174 eap->cmd = eap->arg; 175 (void)find_ex_command(eap, NULL, lookup_scriptitem, NULL); 176 switch (eap->cmdidx) 177 { 178 case CMD_let: 179 case CMD_var: 180 case CMD_final: 181 case CMD_const: 182 case CMD_def: 183 // case CMD_class: 184 is_export = TRUE; 185 do_cmdline(eap->cmd, eap->getline, eap->cookie, 186 DOCMD_VERBOSE + DOCMD_NOWAIT); 187 188 // The command will reset "is_export" when exporting an item. 189 if (is_export) 190 { 191 emsg(_(e_export_with_invalid_argument)); 192 is_export = FALSE; 193 } 194 break; 195 default: 196 emsg(_(e_invalid_command_after_export)); 197 break; 198 } 199 } 200 201 /* 202 * Add a new imported item entry to the current script. 203 */ 204 static imported_T * 205 new_imported(garray_T *gap) 206 { 207 if (ga_grow(gap, 1) == OK) 208 return ((imported_T *)gap->ga_data + gap->ga_len++); 209 return NULL; 210 } 211 212 /* 213 * Free all imported items in script "sid". 214 */ 215 void 216 free_imports_and_script_vars(int sid) 217 { 218 scriptitem_T *si = SCRIPT_ITEM(sid); 219 int idx; 220 221 for (idx = 0; idx < si->sn_imports.ga_len; ++idx) 222 { 223 imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx; 224 225 vim_free(imp->imp_name); 226 } 227 ga_clear(&si->sn_imports); 228 229 free_all_script_vars(si); 230 231 clear_type_list(&si->sn_type_list); 232 } 233 234 /* 235 * Mark all imports as possible to redefine. Used when a script is loaded 236 * again but not cleared. 237 */ 238 void 239 mark_imports_for_reload(int sid) 240 { 241 scriptitem_T *si = SCRIPT_ITEM(sid); 242 int idx; 243 244 for (idx = 0; idx < si->sn_imports.ga_len; ++idx) 245 { 246 imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx; 247 248 imp->imp_flags |= IMP_FLAGS_RELOAD; 249 } 250 } 251 252 /* 253 * ":import Item from 'filename'" 254 * ":import Item as Alias from 'filename'" 255 * ":import {Item} from 'filename'". 256 * ":import {Item as Alias} from 'filename'" 257 * ":import {Item, Item} from 'filename'" 258 * ":import {Item, Item as Alias} from 'filename'" 259 * 260 * ":import * as Name from 'filename'" 261 */ 262 void 263 ex_import(exarg_T *eap) 264 { 265 char_u *cmd_end; 266 evalarg_T evalarg; 267 268 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) 269 { 270 emsg(_(e_import_can_only_be_used_in_script)); 271 return; 272 } 273 fill_evalarg_from_eap(&evalarg, eap, eap->skip); 274 275 cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, 276 &evalarg, NULL); 277 if (cmd_end != NULL) 278 eap->nextcmd = check_nextcmd(cmd_end); 279 clear_evalarg(&evalarg, eap); 280 } 281 282 /* 283 * Find an exported item in "sid" matching the name at "*argp". 284 * When it is a variable return the index. 285 * When it is a user function return "*ufunc". 286 * When not found returns -1 and "*ufunc" is NULL. 287 */ 288 int 289 find_exported( 290 int sid, 291 char_u *name, 292 ufunc_T **ufunc, 293 type_T **type, 294 cctx_T *cctx, 295 int verbose) 296 { 297 int idx = -1; 298 svar_T *sv; 299 scriptitem_T *script = SCRIPT_ITEM(sid); 300 301 // find name in "script" 302 // TODO: also find script-local user function 303 idx = get_script_item_idx(sid, name, 0, cctx); 304 if (idx >= 0) 305 { 306 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; 307 if (!sv->sv_export) 308 { 309 if (verbose) 310 semsg(_(e_item_not_exported_in_script_str), name); 311 return -1; 312 } 313 *type = sv->sv_type; 314 *ufunc = NULL; 315 } 316 else 317 { 318 char_u buffer[200]; 319 char_u *funcname; 320 321 // it could be a user function. 322 if (STRLEN(name) < sizeof(buffer) - 15) 323 funcname = buffer; 324 else 325 { 326 funcname = alloc(STRLEN(name) + 15); 327 if (funcname == NULL) 328 return -1; 329 } 330 funcname[0] = K_SPECIAL; 331 funcname[1] = KS_EXTRA; 332 funcname[2] = (int)KE_SNR; 333 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); 334 *ufunc = find_func(funcname, FALSE, NULL); 335 if (funcname != buffer) 336 vim_free(funcname); 337 338 if (*ufunc == NULL) 339 { 340 if (verbose) 341 semsg(_(e_item_not_found_in_script_str), name); 342 return -1; 343 } 344 } 345 346 return idx; 347 } 348 349 /* 350 * Handle an ":import" command and add the resulting imported_T to "gap", when 351 * not NULL, or script "import_sid" sn_imports. 352 * "cctx" is NULL at the script level. 353 * Returns a pointer to after the command or NULL in case of failure 354 */ 355 char_u * 356 handle_import( 357 char_u *arg_start, 358 garray_T *gap, 359 int import_sid, 360 evalarg_T *evalarg, 361 void *cctx) 362 { 363 char_u *arg = arg_start; 364 char_u *cmd_end = NULL; 365 int ret = FAIL; 366 typval_T tv; 367 int sid = -1; 368 int res; 369 int mult = FALSE; 370 garray_T names; 371 garray_T as_names; 372 373 ga_init2(&names, sizeof(char_u *), 10); 374 ga_init2(&as_names, sizeof(char_u *), 10); 375 if (*arg == '{') 376 { 377 // "import {item, item} from ..." 378 mult = TRUE; 379 arg = skipwhite_and_linebreak(arg + 1, evalarg); 380 } 381 382 for (;;) 383 { 384 char_u *p = arg; 385 int had_comma = FALSE; 386 char_u *as_name = NULL; 387 388 // accept "*" or "Name" 389 if (!mult && arg[0] == '*' && IS_WHITE_OR_NUL(arg[1])) 390 ++arg; 391 else 392 while (eval_isnamec(*arg)) 393 ++arg; 394 if (p == arg) 395 break; 396 if (ga_grow(&names, 1) == FAIL || ga_grow(&as_names, 1) == FAIL) 397 goto erret; 398 ((char_u **)names.ga_data)[names.ga_len] = vim_strnsave(p, arg - p); 399 ++names.ga_len; 400 401 arg = skipwhite_and_linebreak(arg, evalarg); 402 if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2])) 403 { 404 // skip over "as Name "; no line break allowed after "as" 405 arg = skipwhite(arg + 2); 406 p = arg; 407 if (eval_isnamec1(*arg)) 408 while (eval_isnamec(*arg)) 409 ++arg; 410 if (check_defined(p, arg - p, cctx, FALSE) == FAIL) 411 goto erret; 412 as_name = vim_strnsave(p, arg - p); 413 arg = skipwhite_and_linebreak(arg, evalarg); 414 } 415 else if (*arg_start == '*') 416 { 417 emsg(_(e_missing_as_after_star)); 418 goto erret; 419 } 420 // without "as Name" the as_names entry is NULL 421 ((char_u **)as_names.ga_data)[as_names.ga_len] = as_name; 422 ++as_names.ga_len; 423 424 if (!mult) 425 break; 426 if (*arg == ',') 427 { 428 had_comma = TRUE; 429 ++arg; 430 } 431 arg = skipwhite_and_linebreak(arg, evalarg); 432 if (*arg == '}') 433 { 434 ++arg; 435 break; 436 } 437 if (!had_comma) 438 { 439 emsg(_(e_missing_comma_in_import)); 440 goto erret; 441 } 442 } 443 arg = skipwhite_and_linebreak(arg, evalarg); 444 445 if (names.ga_len == 0) 446 { 447 emsg(_(e_syntax_error_in_import)); 448 goto erret; 449 } 450 451 if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4])) 452 { 453 emsg(_(e_missing_from)); 454 goto erret; 455 } 456 457 arg = skipwhite_and_linebreak(arg + 4, evalarg); 458 tv.v_type = VAR_UNKNOWN; 459 // TODO: should we accept any expression? 460 if (*arg == '\'') 461 ret = eval_lit_string(&arg, &tv, TRUE); 462 else if (*arg == '"') 463 ret = eval_string(&arg, &tv, TRUE); 464 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) 465 { 466 emsg(_(e_invalid_string_after_from)); 467 goto erret; 468 } 469 cmd_end = arg; 470 471 /* 472 * find script file 473 */ 474 if (*tv.vval.v_string == '.') 475 { 476 size_t len; 477 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 478 char_u *tail = gettail(si->sn_name); 479 char_u *from_name; 480 481 // Relative to current script: "./name.vim", "../../name.vim". 482 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; 483 from_name = alloc((int)len); 484 if (from_name == NULL) 485 { 486 clear_tv(&tv); 487 goto erret; 488 } 489 vim_strncpy(from_name, si->sn_name, tail - si->sn_name); 490 add_pathsep(from_name); 491 STRCAT(from_name, tv.vval.v_string); 492 simplify_filename(from_name); 493 494 res = do_source(from_name, FALSE, DOSO_NONE, &sid); 495 vim_free(from_name); 496 } 497 else if (mch_isFullName(tv.vval.v_string)) 498 { 499 // Absolute path: "/tmp/name.vim" 500 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); 501 } 502 else 503 { 504 size_t len = 7 + STRLEN(tv.vval.v_string) + 1; 505 char_u *from_name; 506 507 // Find file in "import" subdirs in 'runtimepath'. 508 from_name = alloc((int)len); 509 if (from_name == NULL) 510 { 511 clear_tv(&tv); 512 goto erret; 513 } 514 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); 515 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); 516 vim_free(from_name); 517 } 518 519 if (res == FAIL || sid <= 0) 520 { 521 semsg(_(e_could_not_import_str), tv.vval.v_string); 522 clear_tv(&tv); 523 goto erret; 524 } 525 clear_tv(&tv); 526 527 if (*arg_start == '*') 528 { 529 imported_T *imported; 530 char_u *as_name = ((char_u **)as_names.ga_data)[0]; 531 532 // "import * as That" 533 imported = find_imported(as_name, STRLEN(as_name), cctx); 534 if (imported != NULL && imported->imp_sid == sid) 535 { 536 if (imported->imp_flags & IMP_FLAGS_RELOAD) 537 // import already defined on a previous script load 538 imported->imp_flags &= ~IMP_FLAGS_RELOAD; 539 else 540 { 541 semsg(_(e_name_already_defined_str), as_name); 542 goto erret; 543 } 544 } 545 546 imported = new_imported(gap != NULL ? gap 547 : &SCRIPT_ITEM(import_sid)->sn_imports); 548 if (imported == NULL) 549 goto erret; 550 imported->imp_name = as_name; 551 ((char_u **)as_names.ga_data)[0] = NULL; 552 imported->imp_sid = sid; 553 imported->imp_flags = IMP_FLAGS_STAR; 554 } 555 else 556 { 557 int i; 558 559 arg = arg_start; 560 if (*arg == '{') 561 arg = skipwhite(arg + 1); 562 for (i = 0; i < names.ga_len; ++i) 563 { 564 char_u *name = ((char_u **)names.ga_data)[i]; 565 char_u *as_name = ((char_u **)as_names.ga_data)[i]; 566 size_t len = STRLEN(name); 567 int idx; 568 imported_T *imported; 569 ufunc_T *ufunc = NULL; 570 type_T *type; 571 572 idx = find_exported(sid, name, &ufunc, &type, cctx, TRUE); 573 574 if (idx < 0 && ufunc == NULL) 575 goto erret; 576 577 // If already imported with the same propertis and the 578 // IMP_FLAGS_RELOAD set then we keep that entry. Otherwise create 579 // a new one (and give an error for an existing import). 580 imported = find_imported(name, len, cctx); 581 if (imported != NULL 582 && (imported->imp_flags & IMP_FLAGS_RELOAD) 583 && imported->imp_sid == sid 584 && (idx >= 0 585 ? (equal_type(imported->imp_type, type) 586 && imported->imp_var_vals_idx == idx) 587 : (equal_type(imported->imp_type, ufunc->uf_func_type) 588 && STRCMP(imported->imp_funcname, 589 ufunc->uf_name) == 0))) 590 { 591 imported->imp_flags &= ~IMP_FLAGS_RELOAD; 592 } 593 else 594 { 595 if (check_defined(name, len, cctx, FALSE) == FAIL) 596 goto erret; 597 598 imported = new_imported(gap != NULL ? gap 599 : &SCRIPT_ITEM(import_sid)->sn_imports); 600 if (imported == NULL) 601 goto erret; 602 603 if (as_name == NULL) 604 { 605 imported->imp_name = name; 606 ((char_u **)names.ga_data)[i] = NULL; 607 } 608 else 609 { 610 // "import This as That ..." 611 imported->imp_name = as_name; 612 ((char_u **)as_names.ga_data)[i] = NULL; 613 } 614 imported->imp_sid = sid; 615 if (idx >= 0) 616 { 617 imported->imp_type = type; 618 imported->imp_var_vals_idx = idx; 619 } 620 else 621 { 622 imported->imp_type = ufunc->uf_func_type; 623 imported->imp_funcname = ufunc->uf_name; 624 } 625 } 626 } 627 } 628 erret: 629 ga_clear_strings(&names); 630 ga_clear_strings(&as_names); 631 return cmd_end; 632 } 633 634 /* 635 * Declare a script-local variable without init: "let var: type". 636 * "const" is an error since the value is missing. 637 * Returns a pointer to after the type. 638 */ 639 char_u * 640 vim9_declare_scriptvar(exarg_T *eap, char_u *arg) 641 { 642 char_u *p; 643 char_u *name; 644 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 645 type_T *type; 646 typval_T init_tv; 647 648 if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const) 649 { 650 if (eap->cmdidx == CMD_final) 651 emsg(_(e_final_requires_a_value)); 652 else 653 emsg(_(e_const_requires_a_value)); 654 return arg + STRLEN(arg); 655 } 656 657 // Check for valid starting character. 658 if (!eval_isnamec1(*arg)) 659 { 660 semsg(_(e_invarg2), arg); 661 return arg + STRLEN(arg); 662 } 663 664 for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) 665 if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1)) 666 break; 667 668 if (*p != ':') 669 { 670 emsg(_(e_type_or_initialization_required)); 671 return arg + STRLEN(arg); 672 } 673 if (!VIM_ISWHITE(p[1])) 674 { 675 semsg(_(e_white_space_required_after_str_str), ":", p); 676 return arg + STRLEN(arg); 677 } 678 name = vim_strnsave(arg, p - arg); 679 680 // parse type 681 p = skipwhite(p + 1); 682 type = parse_type(&p, &si->sn_type_list, TRUE); 683 if (type == NULL) 684 { 685 vim_free(name); 686 return p; 687 } 688 689 // Create the variable with 0/NULL value. 690 CLEAR_FIELD(init_tv); 691 if (type->tt_type == VAR_ANY) 692 // A variable of type "any" is not possible, just use zero instead 693 init_tv.v_type = VAR_NUMBER; 694 else 695 init_tv.v_type = type->tt_type; 696 set_var_const(name, type, &init_tv, FALSE, 0, 0); 697 698 vim_free(name); 699 return p; 700 } 701 702 /* 703 * Vim9 part of adding a script variable: add it to sn_all_vars (lookup by name 704 * with a hashtable) and sn_var_vals (lookup by index). 705 * When "create" is TRUE this is a new variable, otherwise find and update an 706 * existing variable. 707 * "flags" can have ASSIGN_FINAL or ASSIGN_CONST. 708 * When "*type" is NULL use "tv" for the type and update "*type". 709 */ 710 void 711 update_vim9_script_var( 712 int create, 713 dictitem_T *di, 714 int flags, 715 typval_T *tv, 716 type_T **type) 717 { 718 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 719 hashitem_T *hi; 720 svar_T *sv; 721 722 if (create) 723 { 724 sallvar_T *newsav; 725 726 // Store a pointer to the typval_T, so that it can be found by index 727 // instead of using a hastab lookup. 728 if (ga_grow(&si->sn_var_vals, 1) == FAIL) 729 return; 730 731 sv = ((svar_T *)si->sn_var_vals.ga_data) + si->sn_var_vals.ga_len; 732 newsav = (sallvar_T *)alloc_clear( 733 sizeof(sallvar_T) + STRLEN(di->di_key)); 734 if (newsav == NULL) 735 return; 736 737 sv->sv_tv = &di->di_tv; 738 sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL 739 : (flags & ASSIGN_CONST) ? ASSIGN_CONST : 0; 740 sv->sv_export = is_export; 741 newsav->sav_var_vals_idx = si->sn_var_vals.ga_len; 742 ++si->sn_var_vals.ga_len; 743 STRCPY(&newsav->sav_key, di->di_key); 744 sv->sv_name = newsav->sav_key; 745 newsav->sav_di = di; 746 newsav->sav_block_id = si->sn_current_block_id; 747 748 hi = hash_find(&si->sn_all_vars.dv_hashtab, newsav->sav_key); 749 if (!HASHITEM_EMPTY(hi)) 750 { 751 sallvar_T *sav = HI2SAV(hi); 752 753 // variable with this name exists in another block 754 while (sav->sav_next != NULL) 755 sav = sav->sav_next; 756 sav->sav_next = newsav; 757 } 758 else 759 // new variable name 760 hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key); 761 } 762 else 763 { 764 sv = find_typval_in_script(&di->di_tv); 765 } 766 if (sv != NULL) 767 { 768 if (*type == NULL) 769 *type = typval2type(tv, get_copyID(), &si->sn_type_list); 770 sv->sv_type = *type; 771 } 772 773 // let ex_export() know the export worked. 774 is_export = FALSE; 775 } 776 777 /* 778 * Hide a script variable when leaving a block. 779 * "idx" is de index in sn_var_vals. 780 * When "func_defined" is non-zero then a function was defined in this block, 781 * the variable may be accessed by it. Otherwise the variable can be cleared. 782 */ 783 void 784 hide_script_var(scriptitem_T *si, int idx, int func_defined) 785 { 786 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 787 hashtab_T *script_ht = get_script_local_ht(); 788 hashtab_T *all_ht = &si->sn_all_vars.dv_hashtab; 789 hashitem_T *script_hi; 790 hashitem_T *all_hi; 791 792 // Remove a variable declared inside the block, if it still exists. 793 // If it was added in a nested block it will already have been removed. 794 // The typval is moved into the sallvar_T. 795 script_hi = hash_find(script_ht, sv->sv_name); 796 all_hi = hash_find(all_ht, sv->sv_name); 797 if (!HASHITEM_EMPTY(script_hi) && !HASHITEM_EMPTY(all_hi)) 798 { 799 dictitem_T *di = HI2DI(script_hi); 800 sallvar_T *sav = HI2SAV(all_hi); 801 sallvar_T *sav_prev = NULL; 802 803 // There can be multiple entries with the same name in different 804 // blocks, find the right one. 805 while (sav != NULL && sav->sav_var_vals_idx != idx) 806 { 807 sav_prev = sav; 808 sav = sav->sav_next; 809 } 810 if (sav != NULL) 811 { 812 if (func_defined) 813 { 814 // move the typval from the dictitem to the sallvar 815 sav->sav_tv = di->di_tv; 816 di->di_tv.v_type = VAR_UNKNOWN; 817 sav->sav_flags = di->di_flags; 818 sav->sav_di = NULL; 819 sv->sv_tv = &sav->sav_tv; 820 } 821 else 822 { 823 if (sav_prev == NULL) 824 hash_remove(all_ht, all_hi); 825 else 826 sav_prev->sav_next = sav->sav_next; 827 sv->sv_name = NULL; 828 vim_free(sav); 829 } 830 delete_var(script_ht, script_hi); 831 } 832 } 833 } 834 835 /* 836 * Free the script variables from "sn_all_vars". 837 */ 838 void 839 free_all_script_vars(scriptitem_T *si) 840 { 841 int todo; 842 hashtab_T *ht = &si->sn_all_vars.dv_hashtab; 843 hashitem_T *hi; 844 sallvar_T *sav; 845 sallvar_T *sav_next; 846 847 hash_lock(ht); 848 todo = (int)ht->ht_used; 849 for (hi = ht->ht_array; todo > 0; ++hi) 850 { 851 if (!HASHITEM_EMPTY(hi)) 852 { 853 --todo; 854 855 // Free the variable. Don't remove it from the hashtab, ht_array 856 // might change then. hash_clear() takes care of it later. 857 sav = HI2SAV(hi); 858 while (sav != NULL) 859 { 860 sav_next = sav->sav_next; 861 if (sav->sav_di == NULL) 862 clear_tv(&sav->sav_tv); 863 vim_free(sav); 864 sav = sav_next; 865 } 866 } 867 } 868 hash_clear(ht); 869 hash_init(ht); 870 871 ga_clear(&si->sn_var_vals); 872 873 // existing commands using script variable indexes are no longer valid 874 si->sn_script_seq = current_sctx.sc_seq; 875 } 876 877 /* 878 * Find the script-local variable that links to "dest". 879 * Returns NULL if not found. 880 */ 881 svar_T * 882 find_typval_in_script(typval_T *dest) 883 { 884 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 885 int idx; 886 887 if (si->sn_version != SCRIPT_VERSION_VIM9) 888 // legacy script doesn't store variable types 889 return NULL; 890 891 // Find the svar_T in sn_var_vals. 892 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) 893 { 894 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 895 896 // If "sv_name" is NULL the variable was hidden when leaving a block, 897 // don't check "sv_tv" then, it might be used for another variable now. 898 if (sv->sv_name != NULL && sv->sv_tv == dest) 899 return sv; 900 } 901 iemsg("find_typval_in_script(): not found"); 902 return NULL; 903 } 904 905 /* 906 * Check if the type of script variable "dest" allows assigning "value". 907 * If needed convert "value" to a bool. 908 */ 909 int 910 check_script_var_type( 911 typval_T *dest, 912 typval_T *value, 913 char_u *name, 914 where_T where) 915 { 916 svar_T *sv = find_typval_in_script(dest); 917 int ret; 918 919 if (sv != NULL) 920 { 921 if (sv->sv_const != 0) 922 { 923 semsg(_(e_readonlyvar), name); 924 return FAIL; 925 } 926 ret = check_typval_type(sv->sv_type, value, where); 927 if (ret == OK && need_convert_to_bool(sv->sv_type, value)) 928 { 929 int val = tv2bool(value); 930 931 clear_tv(value); 932 value->v_type = VAR_BOOL; 933 value->v_lock = 0; 934 value->vval.v_number = val ? VVAL_TRUE : VVAL_FALSE; 935 } 936 return ret; 937 } 938 939 return OK; // not really 940 } 941 942 #endif // FEAT_EVAL 943