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