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 idx = get_script_item_idx(sid, name, 0, cctx); 303 if (idx >= 0) 304 { 305 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; 306 if (!sv->sv_export) 307 { 308 if (verbose) 309 semsg(_(e_item_not_exported_in_script_str), name); 310 return -1; 311 } 312 *type = sv->sv_type; 313 *ufunc = NULL; 314 } 315 else 316 { 317 char_u buffer[200]; 318 char_u *funcname; 319 320 // it could be a user function. 321 if (STRLEN(name) < sizeof(buffer) - 15) 322 funcname = buffer; 323 else 324 { 325 funcname = alloc(STRLEN(name) + 15); 326 if (funcname == NULL) 327 return -1; 328 } 329 funcname[0] = K_SPECIAL; 330 funcname[1] = KS_EXTRA; 331 funcname[2] = (int)KE_SNR; 332 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); 333 *ufunc = find_func(funcname, FALSE, NULL); 334 if (funcname != buffer) 335 vim_free(funcname); 336 337 if (*ufunc == NULL) 338 { 339 if (verbose) 340 semsg(_(e_item_not_found_in_script_str), name); 341 return -1; 342 } 343 else if (((*ufunc)->uf_flags & FC_EXPORT) == 0) 344 { 345 if (verbose) 346 semsg(_(e_item_not_exported_in_script_str), name); 347 *ufunc = NULL; 348 return -1; 349 } 350 } 351 352 return idx; 353 } 354 355 /* 356 * Handle an ":import" command and add the resulting imported_T to "gap", when 357 * not NULL, or script "import_sid" sn_imports. 358 * "cctx" is NULL at the script level. 359 * Returns a pointer to after the command or NULL in case of failure 360 */ 361 char_u * 362 handle_import( 363 char_u *arg_start, 364 garray_T *gap, 365 int import_sid, 366 evalarg_T *evalarg, 367 void *cctx) 368 { 369 char_u *arg = arg_start; 370 char_u *cmd_end = NULL; 371 int ret = FAIL; 372 typval_T tv; 373 int sid = -1; 374 int res; 375 int mult = FALSE; 376 garray_T names; 377 garray_T as_names; 378 379 ga_init2(&names, sizeof(char_u *), 10); 380 ga_init2(&as_names, sizeof(char_u *), 10); 381 if (*arg == '{') 382 { 383 // "import {item, item} from ..." 384 mult = TRUE; 385 arg = skipwhite_and_linebreak(arg + 1, evalarg); 386 } 387 388 for (;;) 389 { 390 char_u *p = arg; 391 int had_comma = FALSE; 392 char_u *as_name = NULL; 393 394 // accept "*" or "Name" 395 if (!mult && arg[0] == '*' && IS_WHITE_OR_NUL(arg[1])) 396 ++arg; 397 else 398 while (eval_isnamec(*arg)) 399 ++arg; 400 if (p == arg) 401 break; 402 if (ga_grow(&names, 1) == FAIL || ga_grow(&as_names, 1) == FAIL) 403 goto erret; 404 ((char_u **)names.ga_data)[names.ga_len] = vim_strnsave(p, arg - p); 405 ++names.ga_len; 406 407 arg = skipwhite_and_linebreak(arg, evalarg); 408 if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2])) 409 { 410 // skip over "as Name "; no line break allowed after "as" 411 arg = skipwhite(arg + 2); 412 p = arg; 413 if (eval_isnamec1(*arg)) 414 while (eval_isnamec(*arg)) 415 ++arg; 416 if (check_defined(p, arg - p, cctx, FALSE) == FAIL) 417 goto erret; 418 as_name = vim_strnsave(p, arg - p); 419 arg = skipwhite_and_linebreak(arg, evalarg); 420 } 421 else if (*arg_start == '*') 422 { 423 emsg(_(e_missing_as_after_star)); 424 goto erret; 425 } 426 // without "as Name" the as_names entry is NULL 427 ((char_u **)as_names.ga_data)[as_names.ga_len] = as_name; 428 ++as_names.ga_len; 429 430 if (!mult) 431 break; 432 if (*arg == ',') 433 { 434 had_comma = TRUE; 435 ++arg; 436 } 437 arg = skipwhite_and_linebreak(arg, evalarg); 438 if (*arg == '}') 439 { 440 ++arg; 441 break; 442 } 443 if (!had_comma) 444 { 445 emsg(_(e_missing_comma_in_import)); 446 goto erret; 447 } 448 } 449 arg = skipwhite_and_linebreak(arg, evalarg); 450 451 if (names.ga_len == 0) 452 { 453 emsg(_(e_syntax_error_in_import)); 454 goto erret; 455 } 456 457 if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4])) 458 { 459 emsg(_(e_missing_from)); 460 goto erret; 461 } 462 463 arg = skipwhite_and_linebreak(arg + 4, evalarg); 464 tv.v_type = VAR_UNKNOWN; 465 // TODO: should we accept any expression? 466 if (*arg == '\'') 467 ret = eval_lit_string(&arg, &tv, TRUE); 468 else if (*arg == '"') 469 ret = eval_string(&arg, &tv, TRUE); 470 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) 471 { 472 emsg(_(e_invalid_string_after_from)); 473 goto erret; 474 } 475 cmd_end = arg; 476 477 /* 478 * find script file 479 */ 480 if (*tv.vval.v_string == '.') 481 { 482 size_t len; 483 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 484 char_u *tail = gettail(si->sn_name); 485 char_u *from_name; 486 487 // Relative to current script: "./name.vim", "../../name.vim". 488 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; 489 from_name = alloc((int)len); 490 if (from_name == NULL) 491 { 492 clear_tv(&tv); 493 goto erret; 494 } 495 vim_strncpy(from_name, si->sn_name, tail - si->sn_name); 496 add_pathsep(from_name); 497 STRCAT(from_name, tv.vval.v_string); 498 simplify_filename(from_name); 499 500 res = do_source(from_name, FALSE, DOSO_NONE, &sid); 501 vim_free(from_name); 502 } 503 else if (mch_isFullName(tv.vval.v_string)) 504 { 505 // Absolute path: "/tmp/name.vim" 506 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); 507 } 508 else 509 { 510 size_t len = 7 + STRLEN(tv.vval.v_string) + 1; 511 char_u *from_name; 512 513 // Find file in "import" subdirs in 'runtimepath'. 514 from_name = alloc((int)len); 515 if (from_name == NULL) 516 { 517 clear_tv(&tv); 518 goto erret; 519 } 520 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); 521 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); 522 vim_free(from_name); 523 } 524 525 if (res == FAIL || sid <= 0) 526 { 527 semsg(_(e_could_not_import_str), tv.vval.v_string); 528 clear_tv(&tv); 529 goto erret; 530 } 531 clear_tv(&tv); 532 533 if (*arg_start == '*') 534 { 535 imported_T *imported; 536 char_u *as_name = ((char_u **)as_names.ga_data)[0]; 537 538 // "import * as That" 539 imported = find_imported(as_name, STRLEN(as_name), cctx); 540 if (imported != NULL && imported->imp_sid == sid) 541 { 542 if (imported->imp_flags & IMP_FLAGS_RELOAD) 543 // import already defined on a previous script load 544 imported->imp_flags &= ~IMP_FLAGS_RELOAD; 545 else 546 { 547 semsg(_(e_name_already_defined_str), as_name); 548 goto erret; 549 } 550 } 551 552 imported = new_imported(gap != NULL ? gap 553 : &SCRIPT_ITEM(import_sid)->sn_imports); 554 if (imported == NULL) 555 goto erret; 556 imported->imp_name = as_name; 557 ((char_u **)as_names.ga_data)[0] = NULL; 558 imported->imp_sid = sid; 559 imported->imp_flags = IMP_FLAGS_STAR; 560 } 561 else 562 { 563 int i; 564 565 arg = arg_start; 566 if (*arg == '{') 567 arg = skipwhite(arg + 1); 568 for (i = 0; i < names.ga_len; ++i) 569 { 570 char_u *name = ((char_u **)names.ga_data)[i]; 571 char_u *as_name = ((char_u **)as_names.ga_data)[i]; 572 size_t len = STRLEN(name); 573 int idx; 574 imported_T *imported; 575 ufunc_T *ufunc = NULL; 576 type_T *type; 577 578 idx = find_exported(sid, name, &ufunc, &type, cctx, TRUE); 579 580 if (idx < 0 && ufunc == NULL) 581 goto erret; 582 583 // If already imported with the same propertis and the 584 // IMP_FLAGS_RELOAD set then we keep that entry. Otherwise create 585 // a new one (and give an error for an existing import). 586 imported = find_imported(name, len, cctx); 587 if (imported != NULL 588 && (imported->imp_flags & IMP_FLAGS_RELOAD) 589 && imported->imp_sid == sid 590 && (idx >= 0 591 ? (equal_type(imported->imp_type, type) 592 && imported->imp_var_vals_idx == idx) 593 : (equal_type(imported->imp_type, ufunc->uf_func_type) 594 && STRCMP(imported->imp_funcname, 595 ufunc->uf_name) == 0))) 596 { 597 imported->imp_flags &= ~IMP_FLAGS_RELOAD; 598 } 599 else 600 { 601 if (check_defined(name, len, cctx, FALSE) == FAIL) 602 goto erret; 603 604 imported = new_imported(gap != NULL ? gap 605 : &SCRIPT_ITEM(import_sid)->sn_imports); 606 if (imported == NULL) 607 goto erret; 608 609 if (as_name == NULL) 610 { 611 imported->imp_name = name; 612 ((char_u **)names.ga_data)[i] = NULL; 613 } 614 else 615 { 616 // "import This as That ..." 617 imported->imp_name = as_name; 618 ((char_u **)as_names.ga_data)[i] = NULL; 619 } 620 imported->imp_sid = sid; 621 if (idx >= 0) 622 { 623 imported->imp_type = type; 624 imported->imp_var_vals_idx = idx; 625 } 626 else 627 { 628 imported->imp_type = ufunc->uf_func_type; 629 imported->imp_funcname = ufunc->uf_name; 630 } 631 } 632 } 633 } 634 erret: 635 ga_clear_strings(&names); 636 ga_clear_strings(&as_names); 637 return cmd_end; 638 } 639 640 /* 641 * Declare a script-local variable without init: "let var: type". 642 * "const" is an error since the value is missing. 643 * Returns a pointer to after the type. 644 */ 645 char_u * 646 vim9_declare_scriptvar(exarg_T *eap, char_u *arg) 647 { 648 char_u *p; 649 char_u *name; 650 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 651 type_T *type; 652 typval_T init_tv; 653 654 if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const) 655 { 656 if (eap->cmdidx == CMD_final) 657 emsg(_(e_final_requires_a_value)); 658 else 659 emsg(_(e_const_requires_a_value)); 660 return arg + STRLEN(arg); 661 } 662 663 // Check for valid starting character. 664 if (!eval_isnamec1(*arg)) 665 { 666 semsg(_(e_invarg2), arg); 667 return arg + STRLEN(arg); 668 } 669 670 for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) 671 if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1)) 672 break; 673 674 if (*p != ':') 675 { 676 emsg(_(e_type_or_initialization_required)); 677 return arg + STRLEN(arg); 678 } 679 if (!VIM_ISWHITE(p[1])) 680 { 681 semsg(_(e_white_space_required_after_str_str), ":", p); 682 return arg + STRLEN(arg); 683 } 684 name = vim_strnsave(arg, p - arg); 685 686 // parse type 687 p = skipwhite(p + 1); 688 type = parse_type(&p, &si->sn_type_list, TRUE); 689 if (type == NULL) 690 { 691 vim_free(name); 692 return p; 693 } 694 695 // Create the variable with 0/NULL value. 696 CLEAR_FIELD(init_tv); 697 if (type->tt_type == VAR_ANY) 698 // A variable of type "any" is not possible, just use zero instead 699 init_tv.v_type = VAR_NUMBER; 700 else 701 init_tv.v_type = type->tt_type; 702 set_var_const(name, type, &init_tv, FALSE, 0, 0); 703 704 vim_free(name); 705 return p; 706 } 707 708 /* 709 * Vim9 part of adding a script variable: add it to sn_all_vars (lookup by name 710 * with a hashtable) and sn_var_vals (lookup by index). 711 * When "create" is TRUE this is a new variable, otherwise find and update an 712 * existing variable. 713 * "flags" can have ASSIGN_FINAL or ASSIGN_CONST. 714 * When "*type" is NULL use "tv" for the type and update "*type". 715 */ 716 void 717 update_vim9_script_var( 718 int create, 719 dictitem_T *di, 720 int flags, 721 typval_T *tv, 722 type_T **type) 723 { 724 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 725 hashitem_T *hi; 726 svar_T *sv; 727 728 if (create) 729 { 730 sallvar_T *newsav; 731 732 // Store a pointer to the typval_T, so that it can be found by index 733 // instead of using a hastab lookup. 734 if (ga_grow(&si->sn_var_vals, 1) == FAIL) 735 return; 736 737 sv = ((svar_T *)si->sn_var_vals.ga_data) + si->sn_var_vals.ga_len; 738 newsav = (sallvar_T *)alloc_clear( 739 sizeof(sallvar_T) + STRLEN(di->di_key)); 740 if (newsav == NULL) 741 return; 742 743 sv->sv_tv = &di->di_tv; 744 sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL 745 : (flags & ASSIGN_CONST) ? ASSIGN_CONST : 0; 746 sv->sv_export = is_export; 747 newsav->sav_var_vals_idx = si->sn_var_vals.ga_len; 748 ++si->sn_var_vals.ga_len; 749 STRCPY(&newsav->sav_key, di->di_key); 750 sv->sv_name = newsav->sav_key; 751 newsav->sav_di = di; 752 newsav->sav_block_id = si->sn_current_block_id; 753 754 hi = hash_find(&si->sn_all_vars.dv_hashtab, newsav->sav_key); 755 if (!HASHITEM_EMPTY(hi)) 756 { 757 sallvar_T *sav = HI2SAV(hi); 758 759 // variable with this name exists in another block 760 while (sav->sav_next != NULL) 761 sav = sav->sav_next; 762 sav->sav_next = newsav; 763 } 764 else 765 // new variable name 766 hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key); 767 } 768 else 769 { 770 sv = find_typval_in_script(&di->di_tv); 771 } 772 if (sv != NULL) 773 { 774 if (*type == NULL) 775 *type = typval2type(tv, get_copyID(), &si->sn_type_list); 776 sv->sv_type = *type; 777 } 778 779 // let ex_export() know the export worked. 780 is_export = FALSE; 781 } 782 783 /* 784 * Hide a script variable when leaving a block. 785 * "idx" is de index in sn_var_vals. 786 * When "func_defined" is non-zero then a function was defined in this block, 787 * the variable may be accessed by it. Otherwise the variable can be cleared. 788 */ 789 void 790 hide_script_var(scriptitem_T *si, int idx, int func_defined) 791 { 792 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 793 hashtab_T *script_ht = get_script_local_ht(); 794 hashtab_T *all_ht = &si->sn_all_vars.dv_hashtab; 795 hashitem_T *script_hi; 796 hashitem_T *all_hi; 797 798 // Remove a variable declared inside the block, if it still exists. 799 // If it was added in a nested block it will already have been removed. 800 // The typval is moved into the sallvar_T. 801 script_hi = hash_find(script_ht, sv->sv_name); 802 all_hi = hash_find(all_ht, sv->sv_name); 803 if (!HASHITEM_EMPTY(script_hi) && !HASHITEM_EMPTY(all_hi)) 804 { 805 dictitem_T *di = HI2DI(script_hi); 806 sallvar_T *sav = HI2SAV(all_hi); 807 sallvar_T *sav_prev = NULL; 808 809 // There can be multiple entries with the same name in different 810 // blocks, find the right one. 811 while (sav != NULL && sav->sav_var_vals_idx != idx) 812 { 813 sav_prev = sav; 814 sav = sav->sav_next; 815 } 816 if (sav != NULL) 817 { 818 if (func_defined) 819 { 820 // move the typval from the dictitem to the sallvar 821 sav->sav_tv = di->di_tv; 822 di->di_tv.v_type = VAR_UNKNOWN; 823 sav->sav_flags = di->di_flags; 824 sav->sav_di = NULL; 825 sv->sv_tv = &sav->sav_tv; 826 } 827 else 828 { 829 if (sav_prev == NULL) 830 hash_remove(all_ht, all_hi); 831 else 832 sav_prev->sav_next = sav->sav_next; 833 sv->sv_name = NULL; 834 vim_free(sav); 835 } 836 delete_var(script_ht, script_hi); 837 } 838 } 839 } 840 841 /* 842 * Free the script variables from "sn_all_vars". 843 */ 844 void 845 free_all_script_vars(scriptitem_T *si) 846 { 847 int todo; 848 hashtab_T *ht = &si->sn_all_vars.dv_hashtab; 849 hashitem_T *hi; 850 sallvar_T *sav; 851 sallvar_T *sav_next; 852 853 hash_lock(ht); 854 todo = (int)ht->ht_used; 855 for (hi = ht->ht_array; todo > 0; ++hi) 856 { 857 if (!HASHITEM_EMPTY(hi)) 858 { 859 --todo; 860 861 // Free the variable. Don't remove it from the hashtab, ht_array 862 // might change then. hash_clear() takes care of it later. 863 sav = HI2SAV(hi); 864 while (sav != NULL) 865 { 866 sav_next = sav->sav_next; 867 if (sav->sav_di == NULL) 868 clear_tv(&sav->sav_tv); 869 vim_free(sav); 870 sav = sav_next; 871 } 872 } 873 } 874 hash_clear(ht); 875 hash_init(ht); 876 877 ga_clear(&si->sn_var_vals); 878 879 // existing commands using script variable indexes are no longer valid 880 si->sn_script_seq = current_sctx.sc_seq; 881 } 882 883 /* 884 * Find the script-local variable that links to "dest". 885 * Returns NULL if not found. 886 */ 887 svar_T * 888 find_typval_in_script(typval_T *dest) 889 { 890 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 891 int idx; 892 893 if (si->sn_version != SCRIPT_VERSION_VIM9) 894 // legacy script doesn't store variable types 895 return NULL; 896 897 // Find the svar_T in sn_var_vals. 898 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) 899 { 900 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 901 902 // If "sv_name" is NULL the variable was hidden when leaving a block, 903 // don't check "sv_tv" then, it might be used for another variable now. 904 if (sv->sv_name != NULL && sv->sv_tv == dest) 905 return sv; 906 } 907 iemsg("find_typval_in_script(): not found"); 908 return NULL; 909 } 910 911 /* 912 * Check if the type of script variable "dest" allows assigning "value". 913 * If needed convert "value" to a bool. 914 */ 915 int 916 check_script_var_type( 917 typval_T *dest, 918 typval_T *value, 919 char_u *name, 920 where_T where) 921 { 922 svar_T *sv = find_typval_in_script(dest); 923 int ret; 924 925 if (sv != NULL) 926 { 927 if (sv->sv_const != 0) 928 { 929 semsg(_(e_readonlyvar), name); 930 return FAIL; 931 } 932 ret = check_typval_type(sv->sv_type, value, where); 933 if (ret == OK && need_convert_to_bool(sv->sv_type, value)) 934 { 935 int val = tv2bool(value); 936 937 clear_tv(value); 938 value->v_type = VAR_BOOL; 939 value->v_lock = 0; 940 value->vval.v_number = val ? VVAL_TRUE : VVAL_FALSE; 941 } 942 return ret; 943 } 944 945 return OK; // not really 946 } 947 948 #endif // FEAT_EVAL 949