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