END
v9.CheckScriptFailure(lines, 'E1069:')
+ # Test for unsupported comment specifier
+ lines =<< trim END
+ vim9script
+ class Something
+ # comment
+ #{
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1170:')
+
lines =<< trim END
vim9script
assert_equal('object<TextPosition>', typename(pos))
END
v9.CheckScriptSuccess(lines)
+
+ # When referencing object methods, space cannot be used after a "."
+ lines =<< trim END
+ vim9script
+ class A
+ def Foo(): number
+ return 10
+ enddef
+ endclass
+ var a = A.new()
+ var v = a. Foo()
+ END
+ v9.CheckScriptFailure(lines, 'E1202:')
+
+ # Using an object without specifying a method or a member variable
+ lines =<< trim END
+ vim9script
+ class A
+ def Foo(): number
+ return 10
+ enddef
+ endclass
+ var a = A.new()
+ var v = a.
+ END
+ v9.CheckScriptFailure(lines, 'E15:')
+
+ # Error when parsing the arguments of an object method.
+ lines =<< trim END
+ vim9script
+ class A
+ def Foo()
+ enddef
+ endclass
+ var a = A.new()
+ var v = a.Foo(,)
+ END
+ v9.CheckScriptFailure(lines, 'E15:')
enddef
def Test_class_defined_twice()
endclass
END
v9.CheckScriptFailure(lines, 'E1330:')
+
+ # Test for initializing an object member with an unknown variable/type
+ lines =<< trim END
+ vim9script
+ class A
+ this.value = init_val
+ endclass
+ END
+ v9.CheckScriptFailureList(lines, ['E121:', 'E1329:'])
enddef
def Test_class_object_member_access()
END
v9.CheckScriptSuccess(lines)
+ # Test for a public member variable name beginning with an underscore
+ lines =<< trim END
+ vim9script
+ class A
+ public this._val = 10
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1332:')
+
lines =<< trim END
vim9script
.x
END
v9.CheckScriptSuccess(lines)
+
+ # Test for "public" cannot be abbreviated
+ lines =<< trim END
+ vim9script
+ class Something
+ pub this.val = 1
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1065:')
+
+ # Test for "public" keyword must be followed by "this" or "static".
+ lines =<< trim END
+ vim9script
+ class Something
+ public val = 1
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1331:')
+
+ # Test for "static" cannot be abbreviated
+ lines =<< trim END
+ vim9script
+ class Something
+ stat this.val = 1
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1065:')
enddef
def Test_class_object_compare()
s.Method(7)
END
v9.CheckScriptFailure(lines, 'E1341: Variable already declared in the class: count')
+
+ # Test for using an invalid type for a member variable
+ lines =<< trim END
+ vim9script
+ class A
+ this.val: xxx
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1010:')
+
+ # Test for no space before or after the '=' when initializing a member
+ # variable
+ lines =<< trim END
+ vim9script
+ class A
+ this.val: number= 10
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1004:')
+ lines =<< trim END
+ vim9script
+ class A
+ this.val: number =10
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1004:')
+
+ # Access a non-existing member
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ var a = A.new()
+ var v = a.bar
+ END
+ v9.CheckScriptFailure(lines, 'E1326:')
enddef
func Test_class_garbagecollect()
assert_equal(2, Value.GetCount())
END
v9.CheckScriptSuccess(lines)
+
+ # Test for cleaning up after a class definition failure when using class
+ # functions.
+ lines =<< trim END
+ vim9script
+ class A
+ static def Foo()
+ enddef
+ aaa
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1318:')
enddef
def Test_class_defcompile()
defcompile C.Fc
END
v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string')
+
+ # Trying to compile a function using a non-existing class variable
+ lines =<< trim END
+ vim9script
+ defcompile x.Foo()
+ END
+ v9.CheckScriptFailure(lines, 'E475:')
+
+ # Trying to compile a function using a variable which is not a class
+ lines =<< trim END
+ vim9script
+ var x: number
+ defcompile x.Foo()
+ END
+ v9.CheckScriptFailure(lines, 'E475:')
+
+ # Trying to compile a function without specifying the name
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ defcompile A.
+ END
+ v9.CheckScriptFailure(lines, 'E475:')
+
+ # Trying to compile a non-existing class object member function
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ var a = A.new()
+ defcompile a.Foo()
+ END
+ v9.CheckScriptFailureList(lines, ['E1334:', 'E475:'])
enddef
def Test_class_object_to_string()
Test()
END
v9.CheckScriptSuccess(lines)
+
+ # Interface name after "extends" doesn't end in a space or NUL character
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ class B extends A"
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1315:')
+
+ # Trailing characters after a class name
+ lines =<< trim END
+ vim9script
+ class A bbb
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E488:')
+
+ # using "implements" with a non-existing class
+ lines =<< trim END
+ vim9script
+ class A implements B
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1346:')
+
+ # using "implements" with a regular class
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ class B implements A
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1347:')
+
+ # using "implements" with a variable
+ lines =<< trim END
+ vim9script
+ var T: number = 10
+ class A implements T
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1347:')
+
+ # all the class methods in an "interface" should be implemented
+ lines =<< trim END
+ vim9script
+ interface A
+ static def Foo()
+ endinterface
+ class B implements A
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1349:')
enddef
def Test_call_interface_method()
assert_equal("object of Success {success: true, value: 'asdf'}", string(v))
END
v9.CheckScriptSuccess(lines)
+
+ # class name after "extends" doesn't end in a space or NUL character
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ class B extends A"
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1315:')
enddef
def Test_using_base_class()
endclass
END
v9.CheckScriptFailure(lines, 'E1316:')
+
+ # Abstract class cannot have a "new" function
+ lines =<< trim END
+ vim9script
+ abstract class Base
+ def new()
+ enddef
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1359:')
enddef
def Test_closure_in_class()
v9.CheckScriptSuccess(lines)
enddef
+" Test for calling methods from three levels of classes
+def Test_multi_level_method_call()
+ var lines =<< trim END
+ vim9script
+
+ var A_func1: number = 0
+ var A_func2: number = 0
+ var A_func3: number = 0
+ var B_func2: number = 0
+ var B_func3: number = 0
+ var C_func3: number = 0
+
+ class A
+ def Func1()
+ A_func1 += 1
+ enddef
+
+ def Func2()
+ A_func2 += 1
+ enddef
+
+ def Func3()
+ A_func3 += 1
+ enddef
+ endclass
+
+ class B extends A
+ def Func2()
+ B_func2 += 1
+ enddef
+
+ def Func3()
+ B_func3 += 1
+ enddef
+ endclass
+
+ class C extends B
+ def Func3()
+ C_func3 += 1
+ enddef
+ endclass
+
+ def A_CallFuncs(a: A)
+ a.Func1()
+ a.Func2()
+ a.Func3()
+ enddef
+
+ def B_CallFuncs(b: B)
+ b.Func1()
+ b.Func2()
+ b.Func3()
+ enddef
+
+ def C_CallFuncs(c: C)
+ c.Func1()
+ c.Func2()
+ c.Func3()
+ enddef
+
+ var cobj = C.new()
+ A_CallFuncs(cobj)
+ B_CallFuncs(cobj)
+ C_CallFuncs(cobj)
+ assert_equal(3, A_func1)
+ assert_equal(0, A_func2)
+ assert_equal(0, A_func3)
+ assert_equal(3, B_func2)
+ assert_equal(0, B_func3)
+ assert_equal(3, C_func3)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using members from three levels of classes
+def Test_multi_level_member_access()
+ var lines =<< trim END
+ vim9script
+
+ class A
+ this.val1: number = 0
+ this.val2: number = 0
+ this.val3: number = 0
+ endclass
+
+ class B extends A
+ this.val2: number = 0
+ this.val3: number = 0
+ endclass
+
+ class C extends B
+ this.val3: number = 0
+ endclass
+
+ def A_members(a: A)
+ a.val1 += 1
+ a.val2 += 1
+ a.val3 += 1
+ enddef
+
+ def B_members(b: B)
+ b.val1 += 1
+ b.val2 += 1
+ b.val3 += 1
+ enddef
+
+ def C_members(c: C)
+ c.val1 += 1
+ c.val2 += 1
+ c.val3 += 1
+ enddef
+
+ var cobj = C.new()
+ A_members(cobj)
+ B_members(cobj)
+ C_members(cobj)
+ assert_equal(3, cobj.val1)
+ assert_equal(3, cobj.val2)
+ assert_equal(3, cobj.val3)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
return table[idx];
}
+/*
+ * Update the interface class lookup table for the member index on the
+ * interface to the member index in the class implementing the interface.
+ * And a lookup table for the object method index on the interface
+ * to the object method index in the class implementing the interface.
+ * This is also used for updating the lookup table for the extended class
+ * hierarchy.
+ */
+ static int
+update_member_method_lookup_table(
+ class_T *ifcl,
+ class_T *cl,
+ garray_T *objmethods,
+ int pobj_method_offset,
+ int is_interface)
+{
+ if (ifcl == NULL)
+ return OK;
+
+ // Table for members.
+ itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
+ + ifcl->class_obj_member_count * sizeof(int));
+ if (if2cl == NULL)
+ return FAIL;
+ if2cl->i2c_next = ifcl->class_itf2class;
+ ifcl->class_itf2class = if2cl;
+ if2cl->i2c_class = cl;
+ if2cl->i2c_is_method = FALSE;
+
+ for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
+ for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
+ {
+ if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
+ cl->class_obj_members[cl_i].ocm_name) == 0)
+ {
+ int *table = (int *)(if2cl + 1);
+ table[if_i] = cl_i;
+ break;
+ }
+ }
+
+ // Table for methods.
+ if2cl = alloc_clear(sizeof(itf2class_T)
+ + ifcl->class_obj_method_count * sizeof(int));
+ if (if2cl == NULL)
+ return FAIL;
+ if2cl->i2c_next = ifcl->class_itf2class;
+ ifcl->class_itf2class = if2cl;
+ if2cl->i2c_class = cl;
+ if2cl->i2c_is_method = TRUE;
+
+ for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
+ {
+ int done = FALSE;
+ for (int cl_i = 0; cl_i < objmethods->ga_len; ++cl_i)
+ {
+ if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
+ ((ufunc_T **)objmethods->ga_data)[cl_i]->uf_name)
+ == 0)
+ {
+ int *table = (int *)(if2cl + 1);
+ table[if_i] = cl_i;
+ done = TRUE;
+ break;
+ }
+ }
+
+ // extended class object method is not overridden by the child class.
+ // Keep the method declared in one of the parent classes in the
+ // lineage.
+ if (!done && !is_interface)
+ {
+ // If "ifcl" is not the immediate parent of "cl", then search in
+ // the intermediate parent classes.
+ if (cl->class_extends != ifcl)
+ {
+ class_T *parent = cl->class_extends;
+ int method_offset = objmethods->ga_len;
+
+ while (!done && parent != NULL && parent != ifcl)
+ {
+
+ for (int cl_i = 0;
+ cl_i < parent->class_obj_method_count_child; ++cl_i)
+ {
+ if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
+ parent->class_obj_methods[cl_i]->uf_name)
+ == 0)
+ {
+ int *table = (int *)(if2cl + 1);
+ table[if_i] = method_offset + cl_i;
+ done = TRUE;
+ break;
+ }
+ }
+ method_offset += parent->class_obj_method_count_child;
+ parent = parent->class_extends;
+ }
+ }
+
+ if (!done)
+ {
+ int *table = (int *)(if2cl + 1);
+ table[if_i] = pobj_method_offset + if_i;
+ }
+ }
+ }
+
+ return OK;
+}
+
/*
* Handle ":class" and ":abstract class" up to ":endclass".
* Handle ":interface" up to ":endinterface".
{
emsg(_(e_cannot_define_new_function_in_abstract_class));
success = FALSE;
+ func_clear_free(uf, FALSE);
break;
}
garray_T *fgap = has_static || is_new
{
semsg(_(e_not_valid_interface_str), impl);
success = FALSE;
+ clear_tv(&tv);
+ break;
}
class_T *ifcl = tv.vval.v_class;
if (cl->class_interface_count > 0 || extends_cl != NULL)
{
- // For each interface add a lookuptable for the member index on the
- // interface to the member index in this class.
- // And a lookuptable for the object method index on the interface
+ // For each interface add a lookup table for the member index on
+ // the interface to the member index in this class.
+ // And a lookup table for the object method index on the interface
// to the object method index in this class.
- // Also do this for the extended class, if any.
- for (int i = 0; i <= cl->class_interface_count; ++i)
+ for (int i = 0; i < cl->class_interface_count; ++i)
{
- class_T *ifcl = i < cl->class_interface_count
- ? cl->class_interfaces_cl[i]
- : extends_cl;
- if (ifcl == NULL)
- continue;
+ class_T *ifcl = cl->class_interfaces_cl[i];
- // Table for members.
- itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
- + ifcl->class_obj_member_count * sizeof(int));
- if (if2cl == NULL)
+ if (update_member_method_lookup_table(ifcl, cl, &objmethods,
+ 0, TRUE) == FAIL)
goto cleanup;
- if2cl->i2c_next = ifcl->class_itf2class;
- ifcl->class_itf2class = if2cl;
- if2cl->i2c_class = cl;
- if2cl->i2c_is_method = FALSE;
-
- for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
- for (int cl_i = 0; cl_i < cl->class_obj_member_count;
- ++cl_i)
- {
- if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
- cl->class_obj_members[cl_i].ocm_name) == 0)
- {
- int *table = (int *)(if2cl + 1);
- table[if_i] = cl_i;
- break;
- }
- }
+ }
- // Table for methods.
- if2cl = alloc_clear(sizeof(itf2class_T)
- + ifcl->class_obj_method_count * sizeof(int));
- if (if2cl == NULL)
- goto cleanup;
- if2cl->i2c_next = ifcl->class_itf2class;
- ifcl->class_itf2class = if2cl;
- if2cl->i2c_class = cl;
- if2cl->i2c_is_method = TRUE;
+ // Update the lookup table for the extended class, if nay
+ if (extends_cl != NULL)
+ {
+ class_T *pclass = extends_cl;
+ int pobj_method_offset = objmethods.ga_len;
- for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
+ // Update the entire lineage of extended classes.
+ while (pclass != NULL)
{
- int done = FALSE;
- for (int cl_i = 0; cl_i < objmethods.ga_len; ++cl_i)
- {
- if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
- ((ufunc_T **)objmethods.ga_data)[cl_i]->uf_name)
- == 0)
- {
- int *table = (int *)(if2cl + 1);
- table[if_i] = cl_i;
- done = TRUE;
- break;
- }
- }
+ if (update_member_method_lookup_table(pclass, cl,
+ &objmethods, pobj_method_offset, FALSE) ==
+ FAIL)
+ goto cleanup;
- if (!done && extends_cl != NULL)
- {
- for (int cl_i = 0;
- cl_i < extends_cl->class_obj_method_count; ++cl_i)
- {
- if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
- extends_cl->class_obj_methods[cl_i]->uf_name)
- == 0)
- {
- int *table = (int *)(if2cl + 1);
- table[if_i] = objmethods.ga_len + cl_i;
- break;
- }
- }
- }
+ pobj_method_offset += pclass->class_obj_method_count_child;
+ pclass = pclass->class_extends;
}
}
}