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