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