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