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