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