summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2024-04-02 20:41:04 +0200
committerChristian Brabandt <cb@256bit.org>2024-04-02 20:41:04 +0200
commitf1750ca0c2ef91c6e4857655ca8fdf8dd8f5abb8 (patch)
treedc071ced955445307763d361fcb4eb0689d8a80e
parent78d742ab8845578f78039ddd71a6444c6929257c (diff)
patch 9.1.0257: Vim9: :call may not find imported class membersv9.1.0257
Problem: Vim9: :call may not find imported class members (mityu) Solution: Set the typval of an imported lval variable correctly (Yegappan Lakshmanan) fixes: #14334 closes: #14386 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
-rw-r--r--src/eval.c112
-rw-r--r--src/testdir/test_vim9_class.vim39
-rw-r--r--src/testdir/test_vim9_import.vim14
-rw-r--r--src/version.c2
4 files changed, 146 insertions, 21 deletions
diff --git a/src/eval.c b/src/eval.c
index 2647e7a591..a4fde0ef29 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1146,6 +1146,91 @@ get_lval_check_access(
}
/*
+ * Get lval information for a variable imported from script "imp_sid". On
+ * success, updates "lp" with the variable name, type, script ID and typval.
+ * The variable name starts at or after "p".
+ * If "rettv" is not NULL it points to the value to be assigned. This used to
+ * match the rhs and lhs types.
+ * Returns a pointer to the character after the variable name if the imported
+ * variable is valid and writable.
+ * Returns NULL if the variable is not exported or typval is not found or the
+ * rhs type doesn't match the lhs type or the variable is not writable.
+ */
+ static char_u *
+get_lval_imported(
+ lval_T *lp,
+ typval_T *rettv,
+ scid_T imp_sid,
+ char_u *p,
+ dictitem_T **dip,
+ int fne_flags,
+ int vim9script)
+{
+ ufunc_T *ufunc;
+ type_T *type = NULL;
+ int cc;
+ int rc = FAIL;
+
+ p = skipwhite(p);
+
+ import_check_sourced_sid(&imp_sid);
+ lp->ll_sid = imp_sid;
+ lp->ll_name = p;
+ p = find_name_end(lp->ll_name, NULL, NULL, fne_flags);
+ lp->ll_name_end = p;
+
+ // check the item is exported
+ cc = *p;
+ *p = NUL;
+ if (find_exported(imp_sid, lp->ll_name, &ufunc, &type, NULL, NULL,
+ TRUE) == -1)
+ goto failed;
+
+ if (vim9script && type != NULL)
+ {
+ where_T where = WHERE_INIT;
+
+ // In a vim9 script, do type check and make sure the variable is
+ // writable.
+ if (check_typval_type(type, rettv, where) == FAIL)
+ goto failed;
+ }
+
+ // Get the typval for the exported item
+ hashtab_T *ht = &SCRIPT_VARS(imp_sid);
+ if (ht == NULL)
+ goto failed;
+
+ dictitem_T *di = find_var_in_ht(ht, 0, lp->ll_name, TRUE);
+ if (di == NULL)
+ // variable is not found
+ goto success;
+
+ *dip = di;
+
+ // Check whether the variable is writable.
+ svar_T *sv = find_typval_in_script(&di->di_tv, imp_sid, FALSE);
+ if (sv != NULL && sv->sv_const != 0)
+ {
+ semsg(_(e_cannot_change_readonly_variable_str), lp->ll_name);
+ goto failed;
+ }
+
+ // check whether variable is locked
+ if (value_check_lock(di->di_tv.v_lock, lp->ll_name, FALSE))
+ goto failed;
+
+ lp->ll_tv = &di->di_tv;
+
+success:
+ rc = OK;
+
+failed:
+ *p = cc;
+ return rc == OK ? p : NULL;
+}
+
+/*
* Get an lval: variable, Dict item or List item that can be assigned a value
* to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
* "name.key", "name.key[expr]" etc.
@@ -1177,7 +1262,7 @@ get_lval(
char_u *p;
char_u *expr_start, *expr_end;
int cc;
- dictitem_T *v;
+ dictitem_T *v = NULL;
typval_T var1;
typval_T var2;
int empty1 = FALSE;
@@ -1311,28 +1396,13 @@ get_lval(
if (*p == '.')
{
imported_T *import = find_imported(lp->ll_name, p - lp->ll_name, TRUE);
-
if (import != NULL)
{
- ufunc_T *ufunc;
- type_T *type;
-
- import_check_sourced_sid(&import->imp_sid);
- lp->ll_sid = import->imp_sid;
- lp->ll_name = skipwhite(p + 1);
- p = find_name_end(lp->ll_name, NULL, NULL, fne_flags);
- lp->ll_name_end = p;
-
- // check the item is exported
- cc = *p;
- *p = NUL;
- if (find_exported(import->imp_sid, lp->ll_name, &ufunc, &type,
- NULL, NULL, TRUE) == -1)
- {
- *p = cc;
+ p++; // skip '.'
+ p = get_lval_imported(lp, rettv, import->imp_sid, p, &v,
+ fne_flags, vim9script);
+ if (p == NULL)
return NULL;
- }
- *p = cc;
}
}
@@ -1352,7 +1422,7 @@ get_lval(
lp->ll_tv = lval_root->lr_tv;
v = NULL;
}
- else
+ else if (lp->ll_tv == NULL)
{
cc = *p;
*p = NUL;
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index cf900f7630..93481d55b8 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -3121,6 +3121,28 @@ def Test_class_import()
v9.CheckScriptSuccess(lines)
enddef
+" Test for importing a class into a legacy script and calling the class method
+def Test_class_method_from_legacy_script()
+ var lines =<< trim END
+ vim9script
+ export class A
+ static var name: string = 'a'
+ static def SetName(n: string)
+ name = n
+ enddef
+ endclass
+ END
+ writefile(lines, 'Xvim9export.vim', 'D')
+
+ lines =<< trim END
+ import './Xvim9export.vim' as vim9
+
+ call s:vim9.A.SetName('b')
+ call assert_equal('b', s:vim9.A.name)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
" Test for implementing an imported interface
def Test_implement_imported_interface()
var lines =<< trim END
@@ -3220,6 +3242,23 @@ def Test_abstract_class()
endclass
END
v9.CheckSourceFailure(lines, 'E1359: Cannot define a "new" method in an abstract class', 4)
+
+ # extending an abstract class with class methods and variables
+ lines =<< trim END
+ vim9script
+ abstract class A
+ static var s: string = 'vim'
+ static def Fn(): list<number>
+ return [10]
+ enddef
+ endclass
+ class B extends A
+ endclass
+ var b = B.new()
+ assert_equal('vim', A.s)
+ assert_equal([10], A.Fn())
+ END
+ v9.CheckScriptSuccess(lines)
enddef
def Test_closure_in_class()
diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim
index fa1aeb10e8..8d68f1e47e 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -2054,6 +2054,13 @@ def Test_import_vim9_from_legacy()
export def GetText(): string
return 'text'
enddef
+ export var exported_nr: number = 22
+ def AddNum(n: number)
+ exported_nr += n
+ enddef
+ export var exportedDict: dict<func> = {Fn: AddNum}
+ export const CONST = 10
+ export final finalVar = 'abc'
END
writefile(vim9_lines, 'Xvim9_export.vim', 'D')
@@ -2072,6 +2079,13 @@ def Test_import_vim9_from_legacy()
" imported symbol is script-local
call assert_equal('exported', s:vim9.exported)
call assert_equal('text', s:vim9.GetText())
+ call s:vim9.exportedDict.Fn(5)
+ call assert_equal(27, s:vim9.exported_nr)
+ call call(s:vim9.exportedDict.Fn, [3])
+ call assert_equal(30, s:vim9.exported_nr)
+ call assert_fails('let s:vim9.CONST = 20', 'E46: Cannot change read-only variable "CONST"')
+ call assert_fails('let s:vim9.finalVar = ""', 'E46: Cannot change read-only variable "finalVar"')
+ call assert_fails('let s:vim9.non_existing_var = 20', 'E1048: Item not found in script: non_existing_var')
END
writefile(legacy_lines, 'Xlegacy_script.vim', 'D')
diff --git a/src/version.c b/src/version.c
index 3d0474aad2..0e54926269 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 257,
+/**/
256,
/**/
255,