summaryrefslogtreecommitdiff
path: root/mnv/src/testdir/test_mnv9_class.mnv
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-04 12:41:27 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-04 12:41:27 +0300
commit4f2d36194b4f299aa7509d815c07121039ea833b (patch)
treef3ded014bad3a4c76ff6a22b8726ebaab68c3d13 /mnv/src/testdir/test_mnv9_class.mnv
parent5b578e70c314723a3cde5c9bfc2be0bf1dadc93b (diff)
downloadProject-Tick-4f2d36194b4f299aa7509d815c07121039ea833b.tar.gz
Project-Tick-4f2d36194b4f299aa7509d815c07121039ea833b.zip
NOISSUE change uvim folder name to mnv
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'mnv/src/testdir/test_mnv9_class.mnv')
-rw-r--r--mnv/src/testdir/test_mnv9_class.mnv11872
1 files changed, 11872 insertions, 0 deletions
diff --git a/mnv/src/testdir/test_mnv9_class.mnv b/mnv/src/testdir/test_mnv9_class.mnv
new file mode 100644
index 0000000000..de6c07fd74
--- /dev/null
+++ b/mnv/src/testdir/test_mnv9_class.mnv
@@ -0,0 +1,11872 @@
+" Test MNV9 classes
+
+import './util/mnv9.mnv' as v9
+
+def Test_class_basic()
+ # Class supported only in "mnv9script"
+ var lines =<< trim END
+ class NotWorking
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in MNV9 script', 1)
+
+ # First character in a class name should be capitalized.
+ lines =<< trim END
+ mnv9script
+ class notWorking
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1314: Class name must start with an uppercase letter: notWorking', 2)
+
+ # Only alphanumeric characters are supported in a class name
+ lines =<< trim END
+ mnv9script
+ class Not@working
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: Not@working', 2)
+
+ # Unsupported keyword (instead of class)
+ lines =<< trim END
+ mnv9script
+ abstract noclass Something
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E475: Invalid argument: noclass Something', 2)
+
+ # Only the complete word "class" should be recognized
+ lines =<< trim END
+ mnv9script
+ abstract classy Something
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E475: Invalid argument: classy Something', 2)
+
+ # The complete "endclass" should be specified.
+ lines =<< trim END
+ mnv9script
+ class Something
+ endcl
+ END
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endcl', 3)
+
+ # Additional words after "endclass"
+ lines =<< trim END
+ mnv9script
+ class Something
+ endclass school's out
+ END
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: school's out", 3)
+
+ # Additional commands after "endclass"
+ lines =<< trim END
+ mnv9script
+ class Something
+ endclass | echo 'done'
+ END
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: | echo 'done'", 3)
+
+ # Additional command after "class name"
+ lines =<< trim END
+ mnv9script
+ class Something | var x = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2)
+
+ # Additional command after "object variable"
+ lines =<< trim END
+ mnv9script
+ class Something
+ var l: list<number> = [] | var y = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: | var y = 10", 3)
+
+ # Additional command after "class variable"
+ lines =<< trim END
+ mnv9script
+ class Something
+ static var d = {a: 10} | var y = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: | var y = 10", 3)
+
+ # Additional command after "object method"
+ lines =<< trim END
+ mnv9script
+ class Something
+ def Foo() | var y = 10
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: | var y = 10", 3)
+
+ # Comments are allowed after an inline block
+ lines =<< trim END
+ mnv9script
+ class Foo
+ static const bar = { # {{{
+ baz: 'qux'
+ } # }}}
+ endclass
+ assert_equal({baz: 'qux'}, Foo.bar)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Try to define a class with the same name as an existing variable
+ lines =<< trim END
+ mnv9script
+ var Something: list<number> = [1]
+ class Thing
+ endclass
+ interface Api
+ endinterface
+ class Something extends Thing implements Api
+ var v1: string = ''
+ def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "Something"', 7)
+
+ # Use old "this." prefixed member variable declaration syntax (without initialization)
+ lines =<< trim END
+ mnv9script
+ class Something
+ this.count: number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.count: number', 3)
+
+ # Use old "this." prefixed member variable declaration syntax (with initialization)
+ lines =<< trim END
+ mnv9script
+ class Something
+ this.count: number = 42
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.count: number = 42', 3)
+
+ # Use old "this." prefixed member variable declaration syntax (type inferred)
+ lines =<< trim END
+ mnv9script
+ class Something
+ this.count = 42
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.count = 42', 3)
+
+ # Use "this" without any member variable name
+ lines =<< trim END
+ mnv9script
+ class Something
+ this
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this', 3)
+
+ # Use "this." without any member variable name
+ lines =<< trim END
+ mnv9script
+ class Something
+ this.
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.', 3)
+
+ # Space between "this" and ".<variable>"
+ lines =<< trim END
+ mnv9script
+ class Something
+ this .count
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this .count', 3)
+
+ # Space between "this." and the member variable name
+ lines =<< trim END
+ mnv9script
+ class Something
+ this. count
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this. count', 3)
+
+ # Use "that" instead of "this"
+ lines =<< trim END
+ mnv9script
+ class Something
+ var count: number
+ that.count
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count', 4)
+
+ # Use "variable" instead of "var" for member variable declaration (without initialization)
+ lines =<< trim END
+ mnv9script
+ class Something
+ variable count: number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: variable count: number', 3)
+
+ # Use "variable" instead of "var" for member variable declaration (with initialization)
+ lines =<< trim END
+ mnv9script
+ class Something
+ variable count: number = 42
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: variable count: number = 42', 3)
+
+ # Use "variable" instead of "var" for member variable declaration (type inferred)
+ lines =<< trim END
+ mnv9script
+ class Something
+ variable count = 42
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: variable count = 42', 3)
+
+ # Use a non-existing member variable in new()
+ lines =<< trim END
+ mnv9script
+ class Something
+ def new()
+ this.state = 0
+ enddef
+ endclass
+ var obj = Something.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "state" not found in object "Something"', 1)
+
+ # Space before ":" in a member variable declaration
+ lines =<< trim END
+ mnv9script
+ class Something
+ var count : number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1059: No white space allowed before colon: count : number', 3)
+
+ # No space after ":" in a member variable declaration
+ lines =<< trim END
+ mnv9script
+ class Something
+ var count:number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E1069: White space required after ':'", 3)
+
+ # Missing ":var" in a "var" member variable declaration (without initialization)
+ lines =<< trim END
+ mnv9script
+ class Something
+ var: number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1317: Invalid object variable declaration: var: number', 3)
+
+ # Missing ":var" in a "var" member variable declaration (with initialization)
+ lines =<< trim END
+ mnv9script
+ class Something
+ var: number = 42
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1317: Invalid object variable declaration: var: number = 42', 3)
+
+ # Missing ":var" in a "var" member variable declaration (type inferred)
+ lines =<< trim END
+ mnv9script
+ class Something
+ var = 42
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1317: Invalid object variable declaration: var = 42', 3)
+
+ # Test for unsupported comment specifier
+ lines =<< trim END
+ mnv9script
+ class Something
+ # comment
+ #{
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1170: Cannot use #{ to start a comment', 3)
+
+ # Test for using class as a bool
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ if A
+ endif
+ END
+ v9.CheckSourceFailure(lines, 'E1405: Class "A" cannot be used as a value', 4)
+
+ # Test for using object as a bool
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ var a = A.new()
+ if a
+ endif
+ END
+ v9.CheckSourceFailure(lines, 'E1320: Using an Object as a Number', 5)
+
+ # Test for using class as a float
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ sort([1.1, A], 'f')
+ END
+ v9.CheckSourceFailure(lines, 'E1405: Class "A" cannot be used as a value', 4)
+
+ # Test for using object as a float
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ var a = A.new()
+ sort([1.1, a], 'f')
+ END
+ v9.CheckSourceFailure(lines, 'E1322: Using an Object as a Float', 5)
+
+ # Test for using class as a string
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ :exe 'call ' .. A
+ END
+ v9.CheckSourceFailure(lines, 'E1405: Class "A" cannot be used as a value', 4)
+
+ # Test for using object as a string
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ var a = A.new()
+ :exe 'call ' .. a
+ END
+ v9.CheckSourceFailure(lines, 'E1324: Using an Object as a String', 5)
+
+ # Test creating a class with member variables and methods, calling a object
+ # method. Check for using type() and typename() with a class and an object.
+ lines =<< trim END
+ mnv9script
+
+ class TextPosition
+ var lnum: number
+ var col: number
+
+ # make a nicely formatted string
+ def ToString(): string
+ return $'({this.lnum}, {this.col})'
+ enddef
+ endclass
+
+ # use the automatically generated new() method
+ var pos = TextPosition.new(2, 12)
+ assert_equal(2, pos.lnum)
+ assert_equal(12, pos.col)
+
+ # call an object method
+ assert_equal('(2, 12)', pos.ToString())
+
+ assert_equal(v:t_class, type(TextPosition))
+ assert_equal(v:t_object, type(pos))
+ assert_equal('class<TextPosition>', typename(TextPosition))
+ assert_equal('object<TextPosition>', typename(pos))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # When referencing object methods, space cannot be used after a "."
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo(): number
+ return 10
+ enddef
+ endclass
+ var a = A.new()
+ var v = a. Foo()
+ END
+ v9.CheckSourceFailure(lines, "E1202: No white space allowed after '.'", 8)
+
+ # Using an object without specifying a method or a member variable
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo(): number
+ return 10
+ enddef
+ endclass
+ var a = A.new()
+ var v = a.
+ END
+ v9.CheckSourceFailure(lines, 'E15: Invalid expression: "a."', 8)
+
+ # Error when parsing the arguments of an object method.
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ enddef
+ endclass
+ var a = A.new()
+ var v = a.Foo(,)
+ END
+ v9.CheckSourceFailure(lines, 'E15: Invalid expression: "a.Foo(,)"', 7)
+
+ # Use a multi-line initialization for a member variable
+ lines =<< trim END
+ mnv9script
+ class A
+ var y = {
+ X: 1
+ }
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Tests for object/class methods in a class
+def Test_class_def_method()
+ # Using the "public" keyword when defining an object method
+ var lines =<< trim END
+ mnv9script
+ class A
+ public def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1388: public keyword not supported for a method', 3)
+
+ # Using the "public" keyword when defining a class method
+ lines =<< trim END
+ mnv9script
+ class A
+ public static def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1388: public keyword not supported for a method', 3)
+
+ # Using the "public" keyword when defining an object protected method
+ lines =<< trim END
+ mnv9script
+ class A
+ public def _Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1388: public keyword not supported for a method', 3)
+
+ # Using the "public" keyword when defining a class protected method
+ lines =<< trim END
+ mnv9script
+ class A
+ public static def _Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1388: public keyword not supported for a method', 3)
+
+ # Using a "def" keyword without an object method name
+ lines =<< trim END
+ mnv9script
+ class A
+ def
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: def', 3)
+
+ # Using a "def" keyword without a class method name
+ lines =<< trim END
+ mnv9script
+ class A
+ static def
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: static def', 3)
+enddef
+
+def Test_class_defined_twice()
+ # class defined twice should fail
+ var lines =<< trim END
+ mnv9script
+ class There
+ endclass
+ class There
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "There"', 4)
+
+ # one class, reload same script twice is OK
+ lines =<< trim END
+ mnv9script
+ class There
+ endclass
+ END
+ writefile(lines, 'XclassTwice.mnv', 'D')
+ source XclassTwice.mnv
+ source XclassTwice.mnv
+enddef
+
+def Test_returning_null_object()
+ # this was causing an internal error
+ var lines =<< trim END
+ mnv9script
+
+ class BufferList
+ def Current(): any
+ return null_object
+ enddef
+ endclass
+
+ var buffers = BufferList.new()
+ echo buffers.Current()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_using_null_class()
+ var lines =<< trim END
+ @_ = null_class.member
+ END
+ v9.CheckDefExecAndScriptFailure(lines, ['E1395: Using a null class', 'E1363: Incomplete type'])
+
+ # Test for using a null class as a value
+ lines =<< trim END
+ mnv9script
+ echo empty(null_class)
+ END
+ v9.CheckSourceFailure(lines, 'E1405: Class "" cannot be used as a value', 2)
+
+ # Test for using a null class with string()
+ lines =<< trim END
+ mnv9script
+ assert_equal('class [unknown]', string(null_class))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for using a null class with type() and typename()
+ lines =<< trim END
+ mnv9script
+ assert_equal(12, type(null_class))
+ assert_equal('class<any>', typename(null_class))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_object_not_set()
+ # Use an uninitialized object in script context
+ var lines =<< trim END
+ mnv9script
+
+ class State
+ var value = 'xyz'
+ endclass
+
+ var state: State
+ var db = {'xyz': 789}
+ echo db[state.value]
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 9)
+
+ # Use an uninitialized object from a def function
+ lines =<< trim END
+ mnv9script
+
+ class Class
+ var id: string
+ def Method1()
+ echo 'Method1' .. this.id
+ enddef
+ endclass
+
+ var obj: Class
+ def Func()
+ obj.Method1()
+ enddef
+ Func()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1)
+
+ # Pass an uninitialized object variable to a "new" function and try to call an
+ # object method.
+ lines =<< trim END
+ mnv9script
+
+ class Background
+ var background = 'dark'
+ endclass
+
+ class Colorscheme
+ var _bg: Background
+
+ def GetBackground(): string
+ return this._bg.background
+ enddef
+ endclass
+
+ var bg: Background # UNINITIALIZED
+ echo Colorscheme.new(bg).GetBackground()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1)
+
+ lines =<< trim END
+ mnv9script
+
+ class Class
+ var id: string
+ def Method1()
+ echo 'Method1' .. this.id
+ enddef
+ endclass
+
+ var obj = null_object
+ def Func()
+ obj.Method1()
+ enddef
+ Func()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1)
+
+ # Reference a object variable through a null class object which is stored in a
+ # variable of type "any".
+ lines =<< trim END
+ mnv9script
+
+ def Z()
+ var o: any = null_object
+ o.v = 4
+ enddef
+ Z()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+ # Do "echom" of a null object variable.
+ lines =<< trim END
+ mnv9script
+
+ def X()
+ var x = null_object
+ echom x
+ enddef
+ X()
+ END
+ v9.CheckSourceFailure(lines, 'E1324: Using an Object as a String', 2)
+
+ # Use a null object variable that mnv wants to force to number.
+ lines =<< trim END
+ mnv9script
+
+ def X()
+ var o = null_object
+ var l = [ 1, o]
+ sort(l, 'N')
+ enddef
+ X()
+ END
+ v9.CheckSourceFailure(lines, 'E1324: Using an Object as a String', 3)
+enddef
+
+" Null object assignment and comparison
+def Test_null_object_assign_compare()
+ var lines =<< trim END
+ mnv9script
+
+ var nullo = null_object
+ def F(): any
+ return nullo
+ enddef
+ assert_equal('object<any>', typename(F()))
+
+ var o0 = F()
+ assert_true(o0 == null_object)
+ assert_true(o0 == null)
+
+ var o1: any = nullo
+ assert_true(o1 == null_object)
+ assert_true(o1 == null)
+
+ def G()
+ var x = null_object
+ enddef
+
+ class C
+ endclass
+ var o2: C
+ assert_true(o2 == null_object)
+ assert_true(o2 == null)
+
+ o2 = null_object
+ assert_true(o2 == null)
+
+ o2 = C.new()
+ assert_true(o2 != null)
+
+ o2 = null_object
+ assert_true(o2 == null)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for object member initialization and disassembly
+def Test_class_member_initializer()
+ var lines =<< trim END
+ mnv9script
+
+ class TextPosition
+ var lnum: number = 1
+ var col: number = 1
+
+ # constructor with only the line number
+ def new(lnum: number)
+ this.lnum = lnum
+ enddef
+ endclass
+
+ var pos = TextPosition.new(3)
+ assert_equal(3, pos.lnum)
+ assert_equal(1, pos.col)
+
+ var instr = execute('disassemble TextPosition.new')
+ assert_match('new\_s*' ..
+ '0 NEW TextPosition size \d\+\_s*' ..
+ '\d PUSHNR 1\_s*' ..
+ '\d STORE_THIS 0\_s*' ..
+ '\d PUSHNR 1\_s*' ..
+ '\d STORE_THIS 1\_s*' ..
+ 'this.lnum = lnum\_s*' ..
+ '\d LOAD arg\[-1]\_s*' ..
+ '\d PUSHNR 0\_s*' ..
+ '\d LOAD $0\_s*' ..
+ '\d\+ STOREINDEX object\_s*' ..
+ '\d\+ RETURN object.*',
+ instr)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_member_any_used_as_object()
+ var lines =<< trim END
+ mnv9script
+
+ class Inner
+ public var value: number = 0
+ endclass
+
+ class Outer
+ var inner: any
+ endclass
+
+ def F(outer: Outer)
+ outer.inner.value = 1
+ enddef
+
+ var inner_obj = Inner.new(0)
+ var outer_obj = Outer.new(inner_obj)
+ F(outer_obj)
+ assert_equal(1, inner_obj.value)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Try modifying a protected variable using an "any" object
+ lines =<< trim END
+ mnv9script
+
+ class Inner
+ var _value: string = ''
+ endclass
+
+ class Outer
+ var inner: any
+ endclass
+
+ def F(outer: Outer)
+ outer.inner._value = 'b'
+ enddef
+
+ var inner_obj = Inner.new('a')
+ var outer_obj = Outer.new(inner_obj)
+ F(outer_obj)
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_value" in class "Inner"', 1)
+
+ # Try modifying a non-existing variable using an "any" object
+ lines =<< trim END
+ mnv9script
+
+ class Inner
+ var value: string = ''
+ endclass
+
+ class Outer
+ var inner: any
+ endclass
+
+ def F(outer: Outer)
+ outer.inner.someval = 'b'
+ enddef
+
+ var inner_obj = Inner.new('a')
+ var outer_obj = Outer.new(inner_obj)
+ F(outer_obj)
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "someval" not found in object "Inner"', 1)
+enddef
+
+" Nested assignment to a object variable which is of another class type
+def Test_assignment_nested_type()
+ var lines =<< trim END
+ mnv9script
+
+ class Inner
+ public var value: number = 0
+ endclass
+
+ class Outer
+ var inner: Inner
+ endclass
+
+ def F(outer: Outer)
+ outer.inner.value = 1
+ enddef
+
+ def Test_assign_to_nested_typed_member()
+ var inner = Inner.new(0)
+ var outer = Outer.new(inner)
+ F(outer)
+ assert_equal(1, inner.value)
+ enddef
+
+ Test_assign_to_nested_typed_member()
+
+ var script_inner = Inner.new(0)
+ var script_outer = Outer.new(script_inner)
+ script_outer.inner.value = 1
+ assert_equal(1, script_inner.value)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Assignment where target item is read only in :def
+ lines =<< trim END
+ mnv9script
+
+ class Inner
+ var value: number = 0
+ endclass
+
+ class Outer
+ var inner: Inner
+ endclass
+
+ def F(outer: Outer)
+ outer.inner.value = 1
+ enddef
+
+ def Test_assign_to_nested_typed_member()
+ var inner = Inner.new(0)
+ var outer = Outer.new(inner)
+ F(outer)
+ assert_equal(1, inner.value)
+ enddef
+
+ Test_assign_to_nested_typed_member()
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "value" in class "Inner" is not writable', 1)
+
+ # Assignment where target item is read only script level
+ lines =<< trim END
+ mnv9script
+
+ class Inner
+ var value: number = 0
+ endclass
+
+ class Outer
+ var inner: Inner
+ endclass
+
+ def F(outer: Outer)
+ outer.inner.value = 1
+ enddef
+
+ var script_inner = Inner.new(0)
+ var script_outer = Outer.new(script_inner)
+ script_outer.inner.value = 1
+ assert_equal(1, script_inner.value)
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "value" in class "Inner" is not writable', 17)
+enddef
+
+def Test_assignment_with_operator()
+ # Use "+=" to assign to a object variable
+ var lines =<< trim END
+ mnv9script
+
+ class Foo
+ public var x: number
+
+ def Add(n: number)
+ this.x += n
+ enddef
+ endclass
+
+ var f = Foo.new(3)
+ f.Add(17)
+ assert_equal(20, f.x)
+
+ def AddToFoo(obj: Foo)
+ obj.x += 3
+ enddef
+
+ AddToFoo(f)
+ assert_equal(23, f.x)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_list_of_objects()
+ var lines =<< trim END
+ mnv9script
+
+ class Foo
+ def Add()
+ enddef
+ endclass
+
+ def ProcessList(fooList: list<Foo>)
+ for foo in fooList
+ foo.Add()
+ endfor
+ enddef
+
+ var l: list<Foo> = [Foo.new()]
+ ProcessList(l)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_expr_after_using_object()
+ var lines =<< trim END
+ mnv9script
+
+ class Something
+ var label: string = ''
+ endclass
+
+ def Foo(): Something
+ var v = Something.new()
+ echo 'in Foo(): ' .. typename(v)
+ return v
+ enddef
+
+ Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_class_default_new()
+ var lines =<< trim END
+ mnv9script
+
+ class TextPosition
+ var lnum: number = 1
+ var col: number = 1
+ endclass
+
+ var pos = TextPosition.new()
+ assert_equal(1, pos.lnum)
+ assert_equal(1, pos.col)
+
+ pos = TextPosition.new(v:none, v:none)
+ assert_equal(1, pos.lnum)
+ assert_equal(1, pos.col)
+
+ pos = TextPosition.new(3, 22)
+ assert_equal(3, pos.lnum)
+ assert_equal(22, pos.col)
+
+ pos = TextPosition.new(v:none, 33)
+ assert_equal(1, pos.lnum)
+ assert_equal(33, pos.col)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Person
+ var name: string
+ var age: number = 42
+ var education: string = "unknown"
+
+ def new(this.name, this.age = v:none, this.education = v:none)
+ enddef
+ endclass
+
+ var piet = Person.new("Piet")
+ assert_equal("Piet", piet.name)
+ assert_equal(42, piet.age)
+ assert_equal("unknown", piet.education)
+
+ var chris = Person.new("Chris", 4, "none")
+ assert_equal("Chris", chris.name)
+ assert_equal(4, chris.age)
+ assert_equal("none", chris.education)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Person
+ var name: string
+ var age: number = 42
+ var education: string = "unknown"
+
+ def new(this.name, this.age = v:none, this.education = v:none)
+ enddef
+ endclass
+
+ var missing = Person.new()
+ END
+ v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: new', 11)
+
+ # Using a specific value to initialize an instance variable in the new()
+ # method.
+ lines =<< trim END
+ mnv9script
+ class A
+ var val: string
+ def new(this.val = 'a')
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none: = 'a'", 4)
+enddef
+
+def Test_class_new_with_object_member()
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ var str: string
+ var num: number
+ def new(this.str, this.num)
+ enddef
+ def newVals(this.str, this.num)
+ enddef
+ endclass
+
+ def Check()
+ try
+ var c = C.new('cats', 2)
+ assert_equal('cats', c.str)
+ assert_equal(2, c.num)
+
+ c = C.newVals('dogs', 4)
+ assert_equal('dogs', c.str)
+ assert_equal(4, c.num)
+ catch
+ assert_report($'Unexpected exception was caught: {v:exception}')
+ endtry
+ enddef
+
+ Check()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var str: string
+ var num: number
+ def new(this.str, this.num)
+ enddef
+ endclass
+
+ def Check()
+ try
+ var c = C.new(1, 2)
+ catch
+ assert_report($'Unexpected exception was caught: {v:exception}')
+ endtry
+ enddef
+
+ Check()
+ END
+ v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number', 2)
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var str: string
+ var num: number
+ def newVals(this.str, this.num)
+ enddef
+ endclass
+
+ def Check()
+ try
+ var c = C.newVals('dogs', 'apes')
+ catch
+ assert_report($'Unexpected exception was caught: {v:exception}')
+ endtry
+ enddef
+
+ Check()
+ END
+ v9.CheckSourceFailure(lines, 'E1013: Argument 2: type mismatch, expected number but got string', 2)
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var str: string
+ def new(str: any)
+ enddef
+ endclass
+
+ def Check()
+ try
+ var c = C.new(1)
+ catch
+ assert_report($'Unexpected exception was caught: {v:exception}')
+ endtry
+ enddef
+
+ Check()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Try using "this." argument in a class method
+ lines =<< trim END
+ mnv9script
+ class A
+ var val = 10
+ static def Foo(this.val: number)
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4)
+
+ # Try using "this." argument in an object method
+ lines =<< trim END
+ mnv9script
+ class A
+ var val = 10
+ def Foo(this.val: number)
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4)
+enddef
+
+def Test_class_object_member_inits()
+ var lines =<< trim END
+ mnv9script
+ class TextPosition
+ var lnum: number
+ var col = 1
+ var addcol: number = 2
+ endclass
+
+ var pos = TextPosition.new()
+ assert_equal(0, pos.lnum)
+ assert_equal(1, pos.col)
+ assert_equal(2, pos.addcol)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class TextPosition
+ var lnum
+ var col = 1
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # If the type is not specified for a member, then it should be set during
+ # object creation and not when defining the class.
+ lines =<< trim END
+ mnv9script
+
+ var init_count = 0
+ def Init(): string
+ init_count += 1
+ return 'foo'
+ enddef
+
+ class A
+ var str1 = Init()
+ var str2: string = Init()
+ var col = 1
+ endclass
+
+ assert_equal(init_count, 0)
+ var a = A.new()
+ assert_equal(init_count, 2)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for initializing an object member with an unknown variable/type
+ lines =<< trim END
+ mnv9script
+ class A
+ var value = init_val
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1001: Variable not found: init_val', 1)
+
+ # Test for initializing an object member with an special type
+ lines =<< trim END
+ mnv9script
+ class A
+ var value: void
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1330: Invalid type used in variable declaration: void', 3)
+enddef
+
+" Test for instance variable access
+def Test_instance_variable_access()
+ var lines =<< trim END
+ mnv9script
+ class Triple
+ var _one = 1
+ var two = 2
+ public var three = 3
+
+ def GetOne(): number
+ return this._one
+ enddef
+ endclass
+
+ var trip = Triple.new()
+ assert_equal(1, trip.GetOne())
+ assert_equal(2, trip.two)
+ assert_equal(3, trip.three)
+ assert_fails('echo trip._one', 'E1333: Cannot access protected variable "_one" in class "Triple"')
+
+ assert_fails('trip._one = 11', 'E1333: Cannot access protected variable "_one" in class "Triple"')
+ assert_fails('trip.two = 22', 'E1335: Variable "two" in class "Triple" is not writable')
+ trip.three = 33
+ assert_equal(33, trip.three)
+
+ assert_fails('trip.four = 4', 'E1326: Variable "four" not found in object "Triple"')
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for a public member variable name beginning with an underscore
+ lines =<< trim END
+ mnv9script
+ class A
+ public var _val = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1332: public variable name cannot start with underscore: public var _val = 10', 3)
+
+ lines =<< trim END
+ mnv9script
+
+ class MyCar
+ var make: string
+ var age = 5
+
+ def new(make_arg: string)
+ this.make = make_arg
+ enddef
+
+ def GetMake(): string
+ return $"make = {this.make}"
+ enddef
+ def GetAge(): number
+ return this.age
+ enddef
+ endclass
+
+ var c = MyCar.new("abc")
+ assert_equal('make = abc', c.GetMake())
+
+ c = MyCar.new("def")
+ assert_equal('make = def', c.GetMake())
+
+ var c2 = MyCar.new("123")
+ assert_equal('make = 123', c2.GetMake())
+
+ def CheckCar()
+ assert_equal("make = def", c.GetMake())
+ assert_equal(5, c.GetAge())
+ enddef
+ CheckCar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class MyCar
+ var make: string
+
+ def new(make_arg: string)
+ this.make = make_arg
+ enddef
+ endclass
+
+ var c = MyCar.new("abc")
+ var c = MyCar.new("def")
+ END
+ v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "c"', 12)
+
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var x: list<number> = []
+
+ def Add(n: number): any
+ this.x->add(n)
+ return this
+ enddef
+ endclass
+
+ echo Foo.new().Add(1).Add(2).x
+ echo Foo.new().Add(1).Add(2)
+ .x
+ echo Foo.new().Add(1)
+ .Add(2).x
+ echo Foo.new()
+ .Add(1).Add(2).x
+ echo Foo.new()
+ .Add(1)
+ .Add(2)
+ .x
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for "public" cannot be abbreviated
+ lines =<< trim END
+ mnv9script
+ class Something
+ pub var val = 1
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: pub var val = 1', 3)
+
+ # Test for "public" keyword must be followed by "var" or "static".
+ lines =<< trim END
+ mnv9script
+ class Something
+ public val = 1
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1331: public must be followed by "var" or "static"', 3)
+
+ # Modify a instance variable using the class name in the script context
+ lines =<< trim END
+ mnv9script
+ class A
+ public var val = 1
+ endclass
+ A.val = 1
+ END
+ v9.CheckSourceFailure(lines, 'E1376: Object variable "val" accessible only using class "A" object', 5)
+
+ # Read a instance variable using the class name in the script context
+ lines =<< trim END
+ mnv9script
+ class A
+ public var val = 1
+ endclass
+ var i = A.val
+ END
+ v9.CheckSourceFailure(lines, 'E1376: Object variable "val" accessible only using class "A" object', 5)
+
+ # Modify a instance variable using the class name in a def function
+ lines =<< trim END
+ mnv9script
+ class A
+ public var val = 1
+ endclass
+ def T()
+ A.val = 1
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1376: Object variable "val" accessible only using class "A" object', 1)
+
+ # Read a instance variable using the class name in a def function
+ lines =<< trim END
+ mnv9script
+ class A
+ public var val = 1
+ endclass
+ def T()
+ var i = A.val
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1376: Object variable "val" accessible only using class "A" object', 1)
+
+ # Access from child class extending a class:
+ lines =<< trim END
+ mnv9script
+ class A
+ var ro_obj_var = 10
+ public var rw_obj_var = 20
+ var _priv_obj_var = 30
+ endclass
+
+ class B extends A
+ def Foo()
+ var x: number
+ x = this.ro_obj_var
+ this.ro_obj_var = 0
+ x = this.rw_obj_var
+ this.rw_obj_var = 0
+ x = this._priv_obj_var
+ this._priv_obj_var = 0
+ enddef
+ endclass
+
+ var b = B.new()
+ b.Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for class variable access
+def Test_class_variable_access()
+ # Test for "static" cannot be abbreviated
+ var lines =<< trim END
+ mnv9script
+ class Something
+ stat var val = 1
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: stat var val = 1', 3)
+
+ # Test for "static" cannot be followed by "public".
+ lines =<< trim END
+ mnv9script
+ class Something
+ static public var val = 1
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 3)
+
+ # A readonly class variable cannot be modified from a child class
+ lines =<< trim END
+ mnv9script
+ class A
+ static var ro_class_var = 40
+ endclass
+
+ class B extends A
+ def Foo()
+ A.ro_class_var = 50
+ enddef
+ endclass
+
+ var b = B.new()
+ b.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "ro_class_var" in class "A" is not writable', 1)
+
+ # A protected class variable cannot be accessed from a child class
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _priv_class_var = 60
+ endclass
+
+ class B extends A
+ def Foo()
+ var i = A._priv_class_var
+ enddef
+ endclass
+
+ var b = B.new()
+ b.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_priv_class_var" in class "A"', 1)
+
+ # A protected class variable cannot be modified from a child class
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _priv_class_var = 60
+ endclass
+
+ class B extends A
+ def Foo()
+ A._priv_class_var = 0
+ enddef
+ endclass
+
+ var b = B.new()
+ b.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_priv_class_var" in class "A"', 1)
+
+ # Access from child class extending a class and from script context
+ lines =<< trim END
+ mnv9script
+ class A
+ static var ro_class_var = 10
+ public static var rw_class_var = 20
+ static var _priv_class_var = 30
+ endclass
+
+ class B extends A
+ def Foo()
+ var x: number
+ x = A.ro_class_var
+ assert_equal(10, x)
+ x = A.rw_class_var
+ assert_equal(25, x)
+ A.rw_class_var = 20
+ assert_equal(20, A.rw_class_var)
+ enddef
+ endclass
+
+ assert_equal(10, A.ro_class_var)
+ assert_equal(20, A.rw_class_var)
+ A.rw_class_var = 25
+ assert_equal(25, A.rw_class_var)
+ var b = B.new()
+ b.Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_class_object_compare()
+ var class_lines =<< trim END
+ mnv9script
+ class Item
+ var nr = 0
+ var name = 'xx'
+ endclass
+ END
+
+ # used at the script level and in a compiled function
+ var test_lines =<< trim END
+ var i1 = Item.new()
+ assert_equal(i1, i1)
+ assert_true(i1 is i1)
+ var i2 = Item.new()
+ assert_equal(i1, i2)
+ assert_false(i1 is i2)
+ var i3 = Item.new(0, 'xx')
+ assert_equal(i1, i3)
+
+ var io1 = Item.new(1, 'xx')
+ assert_notequal(i1, io1)
+ var io2 = Item.new(0, 'yy')
+ assert_notequal(i1, io2)
+ END
+
+ v9.CheckSourceSuccess(class_lines + test_lines)
+ v9.CheckSourceSuccess(
+ class_lines + ['def Test()'] + test_lines + ['enddef', 'Test()'])
+
+ for op in ['>', '>=', '<', '<=', '=~', '!~']
+ var op_lines = [
+ 'var i1 = Item.new()',
+ 'var i2 = Item.new()',
+ 'echo i1 ' .. op .. ' i2',
+ ]
+ v9.CheckSourceFailure(class_lines + op_lines, 'E1153: Invalid operation for object', 8)
+ v9.CheckSourceFailure(class_lines
+ + ['def Test()'] + op_lines + ['enddef', 'Test()'], 'E1153: Invalid operation for object')
+ endfor
+enddef
+
+def Test_object_type()
+ var lines =<< trim END
+ mnv9script
+
+ class One
+ var one = 1
+ endclass
+ class Two
+ var two = 2
+ endclass
+ class TwoMore extends Two
+ var more = 9
+ endclass
+
+ var o: One = One.new()
+ var t: Two = Two.new()
+ var m: TwoMore = TwoMore.new()
+ var tm: Two = TwoMore.new()
+
+ t = m
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class One
+ var one = 1
+ endclass
+ class Two
+ var two = 2
+ endclass
+
+ var o: One = Two.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<One> but got object<Two>', 10)
+
+ lines =<< trim END
+ mnv9script
+
+ interface One
+ def GetMember(): number
+ endinterface
+ class Two implements One
+ var one = 1
+ def GetMember(): number
+ return this.one
+ enddef
+ endclass
+
+ var o: One = Two.new(5)
+ assert_equal(5, o.GetMember())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class Num
+ var n: number = 0
+ endclass
+
+ def Ref(name: string): func(Num): Num
+ return (arg: Num): Num => {
+ return eval(name)(arg)
+ }
+ enddef
+
+ const Fn = Ref('Double')
+ var Double = (m: Num): Num => Num.new(m.n * 2)
+
+ echo Fn(Num.new(4))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_class_member()
+ # check access rules
+ var lines =<< trim END
+ mnv9script
+ class TextPos
+ var lnum = 1
+ var col = 1
+ static var counter = 0
+ static var _secret = 7
+ public static var anybody = 42
+
+ static def AddToCounter(nr: number)
+ counter += nr
+ enddef
+ endclass
+
+ assert_equal(0, TextPos.counter)
+ TextPos.AddToCounter(3)
+ assert_equal(3, TextPos.counter)
+ assert_fails('echo TextPos.noSuchMember', 'E1337: Class variable "noSuchMember" not found in class "TextPos"')
+
+ def GetCounter(): number
+ return TextPos.counter
+ enddef
+ assert_equal(3, GetCounter())
+
+ assert_fails('TextPos.noSuchMember = 2', 'E1337: Class variable "noSuchMember" not found in class "TextPos"')
+ assert_fails('TextPos.counter = 5', 'E1335: Variable "counter" in class "TextPos" is not writable')
+ assert_fails('TextPos.counter += 5', 'E1335: Variable "counter" in class "TextPos" is not writable')
+
+ assert_fails('echo TextPos._secret', 'E1333: Cannot access protected variable "_secret" in class "TextPos"')
+ assert_fails('TextPos._secret = 8', 'E1333: Cannot access protected variable "_secret" in class "TextPos"')
+
+ assert_equal(42, TextPos.anybody)
+ TextPos.anybody = 12
+ assert_equal(12, TextPos.anybody)
+ TextPos.anybody += 5
+ assert_equal(17, TextPos.anybody)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # example in the help
+ lines =<< trim END
+ mnv9script
+ class OtherThing
+ var size: number
+ static var totalSize: number
+
+ def new(this.size)
+ totalSize += this.size
+ enddef
+ endclass
+ assert_equal(0, OtherThing.totalSize)
+ var to3 = OtherThing.new(3)
+ assert_equal(3, OtherThing.totalSize)
+ var to7 = OtherThing.new(7)
+ assert_equal(10, OtherThing.totalSize)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # using static class member twice
+ lines =<< trim END
+ mnv9script
+
+ class HTML
+ static var author: string = 'John Doe'
+
+ static def MacroSubstitute(s: string): string
+ return substitute(s, '{{author}}', author, 'gi')
+ enddef
+ endclass
+
+ assert_equal('some text', HTML.MacroSubstitute('some text'))
+ assert_equal('some text', HTML.MacroSubstitute('some text'))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # access protected member in lambda
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var _x: number = 0
+
+ def Add(n: number): number
+ const F = (): number => this._x + n
+ return F()
+ enddef
+ endclass
+
+ var foo = Foo.new()
+ assert_equal(5, foo.Add(5))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # access protected member in lambda body
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var _x: number = 6
+
+ def Add(n: number): number
+ var Lam = () => {
+ this._x = this._x + n
+ }
+ Lam()
+ return this._x
+ enddef
+ endclass
+
+ var foo = Foo.new()
+ assert_equal(13, foo.Add(7))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # check shadowing
+ lines =<< trim END
+ mnv9script
+
+ class Some
+ static var count = 0
+ def Method(count: number)
+ echo count
+ enddef
+ endclass
+
+ var s = Some.new()
+ s.Method(7)
+ END
+ v9.CheckSourceFailure(lines, 'E1340: Argument already declared in the class: count', 5)
+
+ # Use a local variable in a method with the same name as a class variable
+ lines =<< trim END
+ mnv9script
+
+ class Some
+ static var count = 0
+ def Method(arg: number)
+ var count = 3
+ echo arg count
+ enddef
+ endclass
+
+ var s = Some.new()
+ s.Method(7)
+ END
+ v9.CheckSourceFailure(lines, 'E1341: Variable already declared in the class: count', 1)
+
+ # Test for using an invalid type for a member variable
+ lines =<< trim END
+ mnv9script
+ class A
+ var val: xxx
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1010: Type not recognized: xxx', 3)
+
+ # Test for setting a member on a null object
+ lines =<< trim END
+ mnv9script
+ class A
+ public var val: string
+ endclass
+
+ def F()
+ var obj: A
+ obj.val = ""
+ enddef
+ F()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+ # Test for accessing a member on a null object
+ lines =<< trim END
+ mnv9script
+ class A
+ var val: string
+ endclass
+
+ def F()
+ var obj: A
+ echo obj.val
+ enddef
+ F()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+ # Test for setting a member on a null object, at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public var val: string
+ endclass
+
+ var obj: A
+ obj.val = ""
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 7)
+
+ # Test for accessing a member on a null object, at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ var val: string
+ endclass
+
+ var obj: A
+ echo obj.val
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 7)
+
+ # Test for no space before or after the '=' when initializing a member
+ # variable
+ lines =<< trim END
+ mnv9script
+ class A
+ var val: number= 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E1004: White space required before and after '='", 3)
+ lines =<< trim END
+ mnv9script
+ class A
+ var val: number =10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E1004: White space required before and after '='", 3)
+
+ # Space is not allowed before the object member variable name
+ lines =<< trim END
+ mnv9script
+ class A
+ var n: number = 10
+ endclass
+
+ def Fn()
+ var a = A.new()
+ var y = a. n
+ enddef
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, "E1202: No white space allowed after '.': . n", 2)
+
+ # Access a non-existing member
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ var a = A.new()
+ var v = a.bar
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "bar" not found in object "A"', 5)
+enddef
+
+" These messages should show the defining class of the variable (base class),
+" not the class that did the reference (super class)
+def Test_defining_class_message()
+ var lines =<< trim END
+ mnv9script
+
+ class Base
+ var _v1: list<list<number>>
+ endclass
+
+ class Child extends Base
+ endclass
+
+ var o = Child.new()
+ var x = o._v1
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "Base"', 11)
+ lines =<< trim END
+ mnv9script
+
+ class Base
+ var _v1: list<list<number>>
+ endclass
+
+ class Child extends Base
+ endclass
+
+ def F()
+ var o = Child.new()
+ var x = o._v1
+ enddef
+ F()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "Base"', 2)
+ lines =<< trim END
+ mnv9script
+
+ class Base
+ var v1: list<list<number>>
+ endclass
+
+ class Child extends Base
+ endclass
+
+ var o = Child.new()
+ o.v1 = []
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "v1" in class "Base" is not writable', 11)
+ lines =<< trim END
+ mnv9script
+
+ class Base
+ var v1: list<list<number>>
+ endclass
+
+ class Child extends Base
+ endclass
+
+ def F()
+ var o = Child.new()
+ o.v1 = []
+ enddef
+ F()
+ END
+
+ # Attempt to read a protected variable that is in the middle
+ # of the class hierarchy.
+ v9.CheckSourceFailure(lines, 'E1335: Variable "v1" in class "Base" is not writable', 2)
+ lines =<< trim END
+ mnv9script
+
+ class Base0
+ endclass
+
+ class Base extends Base0
+ var _v1: list<list<number>>
+ endclass
+
+ class Child extends Base
+ endclass
+
+ def F()
+ var o = Child.new()
+ var x = o._v1
+ enddef
+ F()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "Base"', 2)
+
+ # Attempt to read a protected variable that is at the start
+ # of the class hierarchy.
+ lines =<< trim END
+ mnv9script
+
+ class Base0
+ endclass
+
+ class Base extends Base0
+ endclass
+
+ class Child extends Base
+ var _v1: list<list<number>>
+ endclass
+
+ def F()
+ var o = Child.new()
+ var x = o._v1
+ enddef
+ F()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "Child"', 2)
+enddef
+
+func Test_class_garbagecollect()
+ let lines =<< trim END
+ mnv9script
+
+ class Point
+ var p = [2, 3]
+ static var pl = ['a', 'b']
+ static var pd = {a: 'a', b: 'b'}
+ endclass
+
+ echo Point.pl Point.pd
+ call test_garbagecollect_now()
+ echo Point.pl Point.pd
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ let lines =<< trim END
+ mnv9script
+
+ interface View
+ endinterface
+
+ class Widget
+ var view: View
+ endclass
+
+ class MyView implements View
+ var widget: Widget
+
+ def new()
+ # this will result in a circular reference to this object
+ var widget = Widget.new(this)
+ enddef
+ endclass
+
+ var view = MyView.new()
+
+ # overwrite "view", will be garbage-collected next
+ view = MyView.new()
+ test_garbagecollect_now()
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+def Test_class_method()
+ var lines =<< trim END
+ mnv9script
+ class Value
+ var value = 0
+ static var objects = 0
+
+ def new(v: number)
+ this.value = v
+ ++objects
+ enddef
+
+ static def GetCount(): number
+ return objects
+ enddef
+ endclass
+
+ assert_equal(0, Value.GetCount())
+ var v1 = Value.new(2)
+ assert_equal(1, Value.GetCount())
+ var v2 = Value.new(7)
+ assert_equal(2, Value.GetCount())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for cleaning up after a class definition failure when using class
+ # functions.
+ lines =<< trim END
+ mnv9script
+ class A
+ static def Foo()
+ enddef
+ aaa
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: aaa', 5)
+
+ # Test for calling a class method from another class method without the class
+ # name prefix.
+ lines =<< trim END
+ mnv9script
+ class A
+ static var myList: list<number> = [1]
+ static def Foo(n: number)
+ myList->add(n)
+ enddef
+ static def Bar()
+ Foo(2)
+ enddef
+ def Baz()
+ Foo(3)
+ enddef
+ endclass
+ A.Bar()
+ var a = A.new()
+ a.Baz()
+ assert_equal([1, 2, 3], A.myList)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_class_defcompile()
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ def Fo(i: number): string
+ return i
+ enddef
+ endclass
+
+ defcompile C.Fo
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected string but got number', 1)
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ static def Fc(): number
+ return 'x'
+ enddef
+ endclass
+
+ defcompile C.Fc
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got string', 1)
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ static def new()
+ enddef
+ endclass
+
+ defcompile C.new
+ END
+ v9.CheckSourceFailure(lines, 'E1370: Cannot define a "new" method as static', 5)
+
+ # Trying to compile a function using a non-existing class variable
+ lines =<< trim END
+ mnv9script
+ defcompile x.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E475: Invalid argument: x.Foo()', 2)
+
+ # Trying to compile a function using a variable which is not a class
+ lines =<< trim END
+ mnv9script
+ var x: number
+ defcompile x.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E475: Invalid argument: x.Foo()', 3)
+
+ # Trying to compile a function without specifying the name
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ defcompile A.
+ END
+ v9.CheckSourceFailure(lines, 'E475: Invalid argument: A.', 4)
+
+ # Trying to compile a non-existing class object member function
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ var a = A.new()
+ defcompile a.Foo()
+ END
+ v9.CheckSourceFailureList(lines, ['E1326: Variable "Foo" not found in object "A"', 'E475: Invalid argument: a.Foo()'])
+enddef
+
+def Test_class_object_to_string()
+ var lines =<< trim END
+ mnv9script
+ class TextPosition
+ var lnum = 1
+ var col = 22
+ endclass
+
+ assert_equal("class TextPosition", string(TextPosition))
+
+ var pos = TextPosition.new()
+ assert_equal("object of TextPosition {lnum: 1, col: 22}", string(pos))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # check string() with object nesting
+ lines =<< trim END
+ mnv9script
+ class C
+ var nest1: C
+ var nest2: C
+ def Init(n1: C, n2: C)
+ this.nest1 = n1
+ this.nest2 = n2
+ enddef
+ endclass
+
+ var o1 = C.new()
+ var o2 = C.new()
+ o1.Init(o1, o2)
+ o2.Init(o2, o1)
+
+ # The following previously put's mnv into an infinite loop.
+
+ var expect = "object of C {nest1: object of C {...}, nest2: object of C {nest1: object of C {...}, nest2: object of C {...}}}"
+ assert_equal(expect, string(o1))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class B
+ endclass
+
+ class C
+ var b: B
+ var c: C
+ endclass
+
+ var o1 = C.new(B.new(), C.new(B.new()))
+ var expect = "object of C {b: object of B {}, c: object of C {b: object of B {}, c: object of [unknown]}}"
+ assert_equal(expect, string(o1))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_class_used_as_type()
+ var lines =<< trim END
+ mnv9script
+
+ class Point
+ var x = 0
+ var y = 0
+ endclass
+
+ var p: Point
+ p = Point.new(2, 33)
+ assert_equal(2, p.x)
+ assert_equal(33, p.y)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ interface HasX
+ var x: number
+ endinterface
+
+ class Point implements HasX
+ var x = 0
+ var y = 0
+ endclass
+
+ var p: Point
+ p = Point.new(2, 33)
+ var hx = p
+ assert_equal(2, hx.x)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class Point
+ var x = 0
+ var y = 0
+ endclass
+
+ var p: Point
+ p = 'text'
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<Point> but got string', 9)
+enddef
+
+def Test_class_extends()
+ var lines =<< trim END
+ mnv9script
+ class Base
+ var one = 1
+ def GetOne(): number
+ return this.one
+ enddef
+ endclass
+ class Child extends Base
+ var two = 2
+ def GetTotal(): number
+ return this.one + this.two
+ enddef
+ endclass
+ var o = Child.new()
+ assert_equal(1, o.one)
+ assert_equal(2, o.two)
+ assert_equal(1, o.GetOne())
+ assert_equal(3, o.GetTotal())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Base
+ var one = 1
+ endclass
+ class Child extends Base
+ var two = 2
+ endclass
+ var o = Child.new(3, 44)
+ assert_equal(3, o.one)
+ assert_equal(44, o.two)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Base
+ var one = 1
+ endclass
+ class Child extends Base extends Base
+ var two = 2
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"', 5)
+
+ lines =<< trim END
+ mnv9script
+ class Child extends BaseClass
+ var two = 2
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass', 4)
+
+ lines =<< trim END
+ mnv9script
+ var SomeVar = 99
+ class Child extends SomeVar
+ var two = 2
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5)
+
+ lines =<< trim END
+ mnv9script
+ class Child
+ var age: number
+ def ToString(): number
+ return this.age
+ enddef
+ def ToString(): string
+ return this.age
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9)
+
+ lines =<< trim END
+ mnv9script
+ class Base
+ var name: string
+ static def ToString(): string
+ return 'Base class'
+ enddef
+ endclass
+
+ class Child extends Base
+ var age: number
+ def ToString(): string
+ return Base.ToString() .. ': ' .. this.age
+ enddef
+ endclass
+
+ var o = Child.new('John', 42)
+ assert_equal('Base class: 42', o.ToString())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Base
+ var value = 1
+ def new(init: number)
+ this.value = number + 1
+ enddef
+ endclass
+ class Child extends Base
+ def new()
+ this.new(3)
+ enddef
+ endclass
+ var c = Child.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1385: Class method "new" accessible only using class "Child"', 1)
+
+ # base class with more than one object member
+ lines =<< trim END
+ mnv9script
+
+ class Result
+ var success: bool
+ var value: any = null
+ endclass
+
+ class Success extends Result
+ def new(this.value = v:none)
+ this.success = true
+ enddef
+ endclass
+
+ var v = Success.new('asdf')
+ assert_equal("object of Success {success: true, value: 'asdf'}", string(v))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # class name after "extends" doesn't end in a space or NUL character
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B extends A"
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
+enddef
+
+def Test_using_base_class()
+ var lines =<< trim END
+ mnv9script
+
+ class BaseEE
+ def Enter(): any
+ return null
+ enddef
+ def Exit(resource: any): void
+ enddef
+ endclass
+
+ class ChildEE extends BaseEE
+ def Enter(): any
+ return 42
+ enddef
+
+ def Exit(resource: number): void
+ g:result ..= '/exit'
+ enddef
+ endclass
+
+ def With(ee: BaseEE)
+ var r = ee.Enter()
+ try
+ g:result ..= r
+ finally
+ g:result ..= '/finally'
+ ee.Exit(r)
+ endtry
+ enddef
+
+ g:result = ''
+ With(ChildEE.new())
+ assert_equal('42/finally/exit', g:result)
+ END
+ v9.CheckSourceSuccess(lines)
+ unlet g:result
+enddef
+
+" Test for using a method from the super class
+def Test_super_dispatch()
+ # See #15448 and #15463
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ def String(): string
+ return 'A'
+ enddef
+ endclass
+
+ class B extends A
+ def String(): string
+ return super.String()
+ enddef
+ endclass
+
+ class C extends B
+ endclass
+
+ assert_equal('A', C.new().String())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def F(): string
+ return 'AA'
+ enddef
+ endclass
+
+ class B extends A
+ def F(): string
+ return 'BB'
+ enddef
+ def S(): string
+ return super.F()
+ enddef
+ def S0(): string
+ return this.S()
+ enddef
+ endclass
+
+ class C extends B
+ def F(): string
+ return 'CC'
+ enddef
+ def ToB(): string
+ return super.F()
+ enddef
+ endclass
+
+ assert_equal('AA', B.new().S())
+ assert_equal('AA', C.new().S())
+ assert_equal('AA', B.new().S0())
+ assert_equal('AA', C.new().S0())
+
+ assert_equal('BB', C.new().ToB())
+
+ assert_equal('CC', C.new().F())
+ assert_equal('BB', B.new().F())
+ assert_equal('AA', A.new().F())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ var call_chain: list<string>
+
+ abstract class A
+ abstract def _G(): string
+
+ def F(): string
+ call_chain->add('A.F()')
+ return this._G()
+ enddef
+ def _H(): string
+ call_chain->add('A._H()')
+ return this.F()
+ enddef
+ endclass
+
+ class B extends A
+ def _G(): string
+ call_chain->add('B.G()')
+ return 'BBB'
+ enddef
+ def SF(): string
+ call_chain->add('B.SF()')
+ return super._H()
+ enddef
+ endclass
+
+ class C extends B
+ endclass
+
+ class D extends C
+ def SF(): string
+ call_chain->add('D.SF()')
+ return super.SF()
+ enddef
+ endclass
+
+ class E extends D
+ def SF(): string
+ call_chain->add('E.SF()')
+ return super.SF()
+ enddef
+ endclass
+
+ class F extends E
+ def _G(): string
+ call_chain->add('F._G()')
+ return 'FFF'
+ enddef
+ endclass
+
+ # E.new() -> A.F() -> B._G()
+ call_chain = []
+ var o1 = E.new()
+ assert_equal('BBB', o1.F())
+ assert_equal(['A.F()', 'B.G()'], call_chain)
+
+ # F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G()
+ call_chain = []
+ var o2 = F.new()
+ assert_equal('FFF', o2.SF())
+ assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # problems with method dispatch: super -> abstract
+ # https://github.com/Project-Tick/Project-Tick/issues/15514
+ lines =<< trim END
+ mnv9script
+ abstract class B
+ abstract def ToString(): string
+ endclass
+
+ class C extends B
+ def ToString(): string
+ return super.ToString()
+ enddef
+ endclass
+
+ try
+ defcompile C.ToString
+ call assert_false(1, 'command should have failed')
+ catch
+ call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
+ endtry
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # problems with method dispatch: super -> abstract -> concrete
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def ToString()
+ echo 'A'
+ enddef
+ endclass
+
+ abstract class B extends A
+ abstract def ToString()
+ endclass
+
+ class C extends B
+ def ToString()
+ super.ToString()
+ enddef
+ endclass
+
+ try
+ defcompile C.ToString
+ call assert_false(1, 'command should have failed')
+ catch
+ call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
+ endtry
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Invoking a super method and an interface method which have the same name.
+ lines =<< trim END
+ mnv9script
+
+ interface I
+ def ToString(): string
+ endinterface
+
+ # Note that A does not implement I.
+ class A
+ def ToString(): string
+ return 'A'
+ enddef
+ endclass
+
+ class B extends A implements I
+ def ToString(): string
+ return super.ToString()
+ enddef
+ endclass
+
+ def TestI(i: I): string
+ return i.ToString()
+ enddef
+
+ assert_equal('A', B.new().ToString())
+ assert_equal('A', TestI(B.new()))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # super and an abstract class with no abstract methods
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def ToString(): string
+ return 'A'
+ enddef
+ endclass
+
+ # An abstract class with no abstract methods.
+ abstract class B extends A
+ endclass
+
+ class C extends B
+ def ToString(): string
+ return super.ToString()
+ enddef
+ endclass
+
+ def TestA(a: A): string
+ return a.ToString()
+ enddef
+
+ def TestB(b: B): string
+ return b.ToString()
+ enddef
+
+ assert_equal('A', C.new().ToString())
+ assert_equal('A', TestA(A.new()))
+ assert_equal('A', TestA(C.new()))
+ assert_equal('A', TestB(C.new()))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # super and an abstract class with no abstract methods and the initial
+ # implements clause
+ lines =<< trim END
+ mnv9script
+
+ interface I
+ def ToString(): string
+ endinterface
+
+ # Note that A does not implement I.
+ class A
+ def ToString(): string
+ return 'A'
+ enddef
+ endclass
+
+ # An abstract class with no abstract methods.
+ abstract class B extends A implements I
+ endclass
+
+ class C extends B implements I
+ def ToString(): string
+ return super.ToString()
+ enddef
+ endclass
+
+ # Note that A.ToString() is different from I.ToString().
+ def TestA(a: A): string
+ return a.ToString()
+ enddef
+
+ assert_equal('A', C.new().ToString())
+ assert_equal('A', TestA(A.new()))
+ assert_equal('A', TestA(C.new()))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Invoking a class method in the parent class using "super" should fail
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def Fn(): string
+ return 'A'
+ enddef
+ endclass
+
+ class B extends A
+ static def Fn(): string
+ return super.Fn()
+ enddef
+ endclass
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E1325: Method "Fn" not found in class "B"')
+
+ # Missing name after "super" keyword
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B extends A
+ def Fn()
+ var x = super.()
+ enddef
+ endclass
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E1127: Missing name after dot', 1)
+enddef
+
+def Test_class_import()
+ var lines =<< trim END
+ mnv9script
+ export class Animal
+ var kind: string
+ var name: string
+ endclass
+ END
+ writefile(lines, 'Xanimal.mnv', 'D')
+
+ lines =<< trim END
+ mnv9script
+ import './Xanimal.mnv' as animal
+
+ var a: animal.Animal
+ a = animal.Animal.new('fish', 'Eric')
+ assert_equal('fish', a.kind)
+ assert_equal('Eric', a.name)
+
+ var b: animal.Animal = animal.Animal.new('cat', 'Garfield')
+ assert_equal('cat', b.kind)
+ assert_equal('Garfield', b.name)
+ END
+ 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
+ mnv9script
+ export class A
+ static var name: string = 'a'
+ static def SetName(n: string)
+ name = n
+ enddef
+ endclass
+ END
+ writefile(lines, 'Xmnv9export.mnv', 'D')
+
+ lines =<< trim END
+ import './Xmnv9export.mnv' as mnv9
+
+ call s:mnv9.A.SetName('b')
+ call assert_equal('b', s:mnv9.A.name)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for extending an imported class
+def Test_extend_imported_class()
+ var lines =<< trim END
+ mnv9script
+ export class Imp_C1
+ def Fn1(): number
+ return 5
+ enddef
+ endclass
+ END
+ writefile(lines, 'Xextendimportclass.mnv', 'D')
+
+ lines =<< trim END
+ mnv9script
+ import './Xextendimportclass.mnv' as XClass
+
+ class A extends XClass.Imp_C1
+ endclass
+ var a = A.new()
+ assert_equal(5, a.Fn1())
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+def Test_abstract_class()
+ var lines =<< trim END
+ mnv9script
+ abstract class Base
+ var name: string
+ endclass
+ class Person extends Base
+ var age: number
+ endclass
+ var p: Base = Person.new('Peter', 42)
+ assert_equal('Peter', p.name)
+ assert_equal(42, p.age)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ abstract class Base
+ var name: string
+ endclass
+ class Person extends Base
+ var age: number
+ endclass
+ var p = Base.new('Peter')
+ END
+ v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Base"', 8)
+
+ lines =<< trim END
+ abstract class Base
+ var name: string
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in MNV9 script', 1)
+
+ # Test for "abstract" cannot be abbreviated
+ lines =<< trim END
+ mnv9script
+ abs class A
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs class A', 2)
+
+ # Additional commands after "abstract class"
+ lines =<< trim END
+ mnv9script
+ abstract class Something | var x = []
+ endclass
+ END
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = []", 2)
+
+ # Abstract class cannot have a "new" function
+ lines =<< trim END
+ mnv9script
+ abstract class Base
+ def new()
+ enddef
+ 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
+ mnv9script
+ abstract class A
+ static var s: string = 'mnv'
+ static def Fn(): list<number>
+ return [10]
+ enddef
+ endclass
+ class B extends A
+ endclass
+ var b = B.new()
+ assert_equal('mnv', A.s)
+ assert_equal([10], A.Fn())
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+def Test_closure_in_class()
+ var lines =<< trim END
+ mnv9script
+
+ class Foo
+ var y: list<string> = ['B']
+
+ def new()
+ g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1)
+ enddef
+ endclass
+
+ Foo.new()
+ assert_equal(['A'], g:result)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_construct_object_from_legacy()
+ # Cannot directly invoke constructor from legacy
+ var lines =<< trim END
+ mnv9script
+
+ var newCalled = false
+
+ class A
+ def new(arg: string)
+ newCalled = true
+ enddef
+ endclass
+
+ export def CreateA(...args: list<any>): A
+ return call(A.new, args)
+ enddef
+
+ g:P = CreateA
+ legacy call g:P('some_arg')
+ assert_equal(true, newCalled)
+ unlet g:P
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ var newCalled = false
+
+ class A
+ static def CreateA(options = {}): any
+ return A.new()
+ enddef
+ def new()
+ newCalled = true
+ enddef
+ endclass
+
+ g:P = A.CreateA
+ legacy call g:P()
+ assert_equal(true, newCalled)
+ unlet g:P
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # This also tests invoking "new()" with "call"
+ lines =<< trim END
+ mnv9script
+
+ var createdObject: any
+
+ class A
+ var val1: number
+ var val2: number
+ static def CreateA(...args: list<any>): any
+ createdObject = call(A.new, args)
+ return createdObject
+ enddef
+ endclass
+
+ g:P = A.CreateA
+ legacy call g:P(3, 5)
+ assert_equal(3, createdObject.val1)
+ assert_equal(5, createdObject.val2)
+ legacy call g:P()
+ assert_equal(0, createdObject.val1)
+ assert_equal(0, createdObject.val2)
+ legacy call g:P(7)
+ assert_equal(7, createdObject.val1)
+ assert_equal(0, createdObject.val2)
+ unlet g:P
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_defer_with_object()
+ var lines =<< trim END
+ mnv9script
+
+ class CWithEE
+ def Enter()
+ g:result ..= "entered/"
+ enddef
+ def Exit()
+ g:result ..= "exited"
+ enddef
+ endclass
+
+ def With(ee: CWithEE, F: func)
+ ee.Enter()
+ defer ee.Exit()
+ F()
+ enddef
+
+ g:result = ''
+ var obj = CWithEE.new()
+ obj->With(() => {
+ g:result ..= "called/"
+ })
+ assert_equal('entered/called/exited', g:result)
+ END
+ v9.CheckSourceSuccess(lines)
+ unlet g:result
+
+ lines =<< trim END
+ mnv9script
+
+ class BaseWithEE
+ def Enter()
+ g:result ..= "entered-base/"
+ enddef
+ def Exit()
+ g:result ..= "exited-base"
+ enddef
+ endclass
+
+ class CWithEE extends BaseWithEE
+ def Enter()
+ g:result ..= "entered-child/"
+ enddef
+ def Exit()
+ g:result ..= "exited-child"
+ enddef
+ endclass
+
+ def With(ee: BaseWithEE, F: func)
+ ee.Enter()
+ defer ee.Exit()
+ F()
+ enddef
+
+ g:result = ''
+ var obj = CWithEE.new()
+ obj->With(() => {
+ g:result ..= "called/"
+ })
+ assert_equal('entered-child/called/exited-child', g:result)
+ END
+ v9.CheckSourceSuccess(lines)
+ unlet g:result
+enddef
+
+" The following test used to crash MNV (Github issue #12676)
+def Test_extends_method_crashes_mnv()
+ var lines =<< trim END
+ mnv9script
+
+ class Observer
+ endclass
+
+ class Property
+ var value: any
+
+ def Set(v: any)
+ if v != this.value
+ this.value = v
+ endif
+ enddef
+
+ def Register(observer: Observer)
+ enddef
+ endclass
+
+ class Bool extends Property
+ var value2: bool
+ endclass
+
+ def Observe(obj: Property, who: Observer)
+ obj.Register(who)
+ enddef
+
+ var p = Bool.new(false)
+ var myObserver = Observer.new()
+
+ Observe(p, myObserver)
+
+ p.Set(true)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for calling a method in a class that is extended
+def Test_call_method_in_extended_class()
+ var lines =<< trim END
+ mnv9script
+
+ var prop_init_called = false
+ var prop_register_called = false
+
+ class Property
+ def Init()
+ prop_init_called = true
+ enddef
+
+ def Register()
+ prop_register_called = true
+ enddef
+ endclass
+
+ class Bool extends Property
+ endclass
+
+ def Observe(obj: Property)
+ obj.Register()
+ enddef
+
+ var p = Property.new()
+ Observe(p)
+
+ p.Init()
+ assert_true(prop_init_called)
+ assert_true(prop_register_called)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_instanceof()
+ var lines =<< trim END
+ mnv9script
+
+ class Base1
+ endclass
+
+ class Base2 extends Base1
+ endclass
+
+ interface Intf1
+ endinterface
+
+ class Mix1 implements Intf1
+ endclass
+
+ class Base3 extends Mix1
+ endclass
+
+ type AliasBase1 = Base1
+ type AliasBase2 = Base2
+ type AliasIntf1 = Intf1
+ type AliasMix1 = Mix1
+
+ var b1 = Base1.new()
+ var b2 = Base2.new()
+ var b3 = Base3.new()
+
+ assert_true(instanceof(b1, Base1))
+ assert_true(instanceof(b2, Base1))
+ assert_false(instanceof(b1, Base2))
+ assert_true(instanceof(b3, Mix1))
+ assert_true(instanceof(b3, Base1, Base2, Intf1))
+
+ assert_true(instanceof(b1, AliasBase1))
+ assert_true(instanceof(b2, AliasBase1))
+ assert_false(instanceof(b1, AliasBase2))
+ assert_true(instanceof(b3, AliasMix1))
+ assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1))
+
+ def Foo()
+ var a1 = Base1.new()
+ var a2 = Base2.new()
+ var a3 = Base3.new()
+
+ assert_true(instanceof(a1, Base1))
+ assert_true(instanceof(a2, Base1))
+ assert_false(instanceof(a1, Base2))
+ assert_true(instanceof(a3, Mix1))
+ assert_true(instanceof(a3, Base1, Base2, Intf1))
+
+ assert_true(instanceof(a1, AliasBase1))
+ assert_true(instanceof(a2, AliasBase1))
+ assert_false(instanceof(a1, AliasBase2))
+ assert_true(instanceof(a3, AliasMix1))
+ assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1))
+ enddef
+ Foo()
+
+ var o_null: Base1
+ assert_false(instanceof(o_null, Base1))
+
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class Base1
+ endclass
+ instanceof(Base1.new())
+ END
+ v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')
+
+ lines =<< trim END
+ mnv9script
+
+ class Base1
+ endclass
+ def F()
+ instanceof(Base1.new())
+ enddef
+ F()
+ END
+ v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')
+
+ lines =<< trim END
+ mnv9script
+
+ class Base1
+ endclass
+
+ class Base2
+ endclass
+
+ var o = Base2.new()
+ instanceof(o, Base1, Base2, 3)
+ END
+ v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10)
+
+ lines =<< trim END
+ mnv9script
+
+ class Base1
+ endclass
+
+ class Base2
+ endclass
+
+ def F()
+ var o = Base2.new()
+ instanceof(o, Base1, Base2, 3)
+ enddef
+ F()
+ END
+ v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4')
+enddef
+
+" Test for calling a method in the parent class that is extended partially.
+" This used to fail with the 'E118: Too many arguments for function: Text' error
+" message (Github issue #12524).
+def Test_call_method_in_parent_class()
+ var lines =<< trim END
+ mnv9script
+
+ class Widget
+ var _lnum: number = 1
+
+ def SetY(lnum: number)
+ this._lnum = lnum
+ enddef
+
+ def Text(): string
+ return ''
+ enddef
+ endclass
+
+ class Foo extends Widget
+ def Text(): string
+ return '<Foo>'
+ enddef
+ endclass
+
+ def Stack(w1: Widget, w2: Widget): list<Widget>
+ w1.SetY(1)
+ w2.SetY(2)
+ return [w1, w2]
+ enddef
+
+ var foo1 = Foo.new()
+ var foo2 = Foo.new()
+ var l = Stack(foo1, foo2)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for calling methods from three levels of classes
+def Test_multi_level_method_call()
+ var lines =<< trim END
+ mnv9script
+
+ 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.CheckSourceSuccess(lines)
+enddef
+
+" Test for using members from three levels of classes
+def Test_multi_level_member_access()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ public var val1: number = 0
+ endclass
+
+ class B extends A
+ public var val2: number = 0
+ endclass
+
+ class C extends B
+ public var val3: number = 0
+ endclass
+
+ def A_members(a: A)
+ a.val1 += 1
+ enddef
+
+ def B_members(b: B)
+ b.val1 += 1
+ b.val2 += 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(2, cobj.val2)
+ assert_equal(1, cobj.val3)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test expansion of <stack> with class methods.
+def Test_stack_expansion_with_methods()
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ def M1()
+ F0()
+ enddef
+ endclass
+
+ def F0()
+ assert_match('<SNR>\d\+_F\[1\]\.\.<SNR>\d\+_C\.M1\[1\]\.\.<SNR>\d\+_F0\[1\]$', expand('<stack>'))
+ enddef
+
+ def F()
+ C.new().M1()
+ enddef
+
+ F()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test the return type of the new() constructor
+def Test_new_return_type()
+ # new() uses the default return type and there is no return statement
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ var _bufnr: number
+
+ def new(this._bufnr)
+ if !bufexists(this._bufnr)
+ this._bufnr = -1
+ endif
+ enddef
+ endclass
+
+ var c = C.new(12345)
+ assert_equal('object<C>', typename(c))
+
+ var v1: C
+ v1 = C.new(12345)
+ assert_equal('object<C>', typename(v1))
+
+ def F()
+ var v2: C
+ v2 = C.new(12345)
+ assert_equal('object<C>', typename(v2))
+ enddef
+ F()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # new() uses the default return type and an empty 'return' statement
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var _bufnr: number
+
+ def new(this._bufnr)
+ if !bufexists(this._bufnr)
+ this._bufnr = -1
+ return
+ endif
+ enddef
+ endclass
+
+ var c = C.new(12345)
+ assert_equal('object<C>', typename(c))
+
+ var v1: C
+ v1 = C.new(12345)
+ assert_equal('object<C>', typename(v1))
+
+ def F()
+ var v2: C
+ v2 = C.new(12345)
+ assert_equal('object<C>', typename(v2))
+ enddef
+ F()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # new() uses "any" return type and returns "this"
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var _bufnr: number
+
+ def new(this._bufnr): any
+ if !bufexists(this._bufnr)
+ this._bufnr = -1
+ return this
+ endif
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 11)
+
+ # new() uses 'Dict' return type and returns a Dict
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var _state: dict<any>
+
+ def new(): dict<any>
+ this._state = {}
+ return this._state
+ enddef
+ endclass
+
+ var c = C.new()
+ assert_equal('object<C>', typename(c))
+ END
+ v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 9)
+enddef
+
+" Test for checking a member initialization type at run time.
+def Test_runtime_type_check_for_member_init()
+ var lines =<< trim END
+ mnv9script
+
+ var retnum: bool = false
+
+ def F(): any
+ retnum = !retnum
+ if retnum
+ return 1
+ else
+ return "hello"
+ endif
+ enddef
+
+ class C
+ var _foo: bool = F()
+ endclass
+
+ var c1 = C.new()
+ var c2 = C.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0)
+enddef
+
+" Test for locking a variable referring to an object and reassigning to another
+" object.
+def Test_lockvar_object()
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ var val: number
+ def new(this.val)
+ enddef
+ endclass
+
+ var some_dict: dict<C> = { a: C.new(1), b: C.new(2), c: C.new(3), }
+ lockvar 2 some_dict
+
+ var current: C
+ current = some_dict['c']
+ assert_equal(3, current.val)
+ current = some_dict['b']
+ assert_equal(2, current.val)
+
+ def F()
+ current = some_dict['c']
+ enddef
+
+ def G()
+ current = some_dict['b']
+ enddef
+
+ F()
+ assert_equal(3, current.val)
+ G()
+ assert_equal(2, current.val)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test trying to lock an object variable from various places
+def Test_lockvar_object_variable()
+ # An object variable lockvar has several cases:
+ # object method, scriptlevel, scriplevel from :def, :def arg
+ # method arg, static method arg.
+ # Also different depths
+
+ #
+ # lockvar of read-only object variable
+ #
+
+ # read-only lockvar from object method
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ var val1: number
+ def Lock()
+ lockvar this.val1
+ enddef
+ endclass
+ var o = C.new(3)
+ o.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"')
+
+ # read-only lockvar from scriptlevel
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var val2: number
+ endclass
+ var o = C.new(3)
+ lockvar o.val2
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "val2" in class "C" is not writable')
+
+ # read-only lockvar of scriptlevel variable from def
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var val3: number
+ endclass
+ var o = C.new(3)
+ def Lock()
+ lockvar o.val3
+ enddef
+ Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable')
+
+ # read-only lockvar of def argument variable
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var val4: number
+ endclass
+ def Lock(o: C)
+ lockvar o.val4
+ enddef
+ Lock(C.new(3))
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable')
+
+ # read-only lockvar from object method arg
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var val5: number
+ def Lock(c: C)
+ lockvar c.val5
+ enddef
+ endclass
+ var o = C.new(3)
+ o.Lock(C.new(5))
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"')
+
+ # read-only lockvar from class method arg
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var val6: number
+ static def Lock(c: C)
+ lockvar c.val6
+ enddef
+ endclass
+ var o = C.new(3)
+ C.Lock(o)
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"')
+
+ #
+ # lockvar of public object variable
+ #
+
+ # lockvar from object method
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var val1: number
+ def Lock()
+ lockvar this.val1
+ enddef
+ endclass
+ var o = C.new(3)
+ o.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1)
+
+ # lockvar from scriptlevel
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var val2: number
+ endclass
+ var o = C.new(3)
+ lockvar o.val2
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val2" in class "C"', 7)
+
+ # lockvar of scriptlevel variable from def
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var val3: number
+ endclass
+ var o = C.new(3)
+ def Lock()
+ lockvar o.val3
+ enddef
+ Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1)
+
+ # lockvar of def argument variable
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var val4: number
+ endclass
+ def Lock(o: C)
+ lockvar o.val4
+ enddef
+ Lock(C.new(3))
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val4" in class "C"', 1)
+
+ # lockvar from object method arg
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var val5: number
+ def Lock(c: C)
+ lockvar c.val5
+ enddef
+ endclass
+ var o = C.new(3)
+ o.Lock(C.new(5))
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"', 1)
+
+ # lockvar from class method arg
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var val6: number
+ static def Lock(c: C)
+ lockvar c.val6
+ enddef
+ endclass
+ var o = C.new(3)
+ C.Lock(o)
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"', 1)
+enddef
+
+" Test trying to lock a class variable from various places
+def Test_lockvar_class_variable()
+
+ # lockvar bare static from object method
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval1: number
+ def Lock()
+ lockvar sval1
+ enddef
+ endclass
+ var o = C.new()
+ o.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1)
+
+ # lockvar C.static from object method
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval2: number
+ def Lock()
+ lockvar C.sval2
+ enddef
+ endclass
+ var o = C.new()
+ o.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1)
+
+ # lockvar bare static from class method
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval3: number
+ static def Lock()
+ lockvar sval3
+ enddef
+ endclass
+ C.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1)
+
+ # lockvar C.static from class method
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval4: number
+ static def Lock()
+ lockvar C.sval4
+ enddef
+ endclass
+ C.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1)
+
+ # lockvar C.static from script level
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval5: number
+ endclass
+ lockvar C.sval5
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6)
+
+ # lockvar o.static from script level
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval6: number
+ endclass
+ var o = C.new()
+ lockvar o.sval6
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7)
+enddef
+
+" Test locking an argument to :def
+def Test_lockvar_argument()
+ # Lockvar a function arg
+ var lines =<< trim END
+ mnv9script
+
+ def Lock(val: any)
+ lockvar val
+ enddef
+
+ var d = {a: 1, b: 2}
+ Lock(d)
+
+ d->extend({c: 3})
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument')
+
+ # Lockvar a function arg. Verify "sval" is interpreted as argument and not a
+ # class member in "C". This tests lval_root_is_arg.
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval: list<number>
+ endclass
+
+ def Lock2(sval: any)
+ lockvar sval
+ enddef
+
+ var o = C.new()
+ Lock2(o)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Lock a class.
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval: list<number>
+ endclass
+
+ def Lock2(sval: any)
+ lockvar sval
+ enddef
+
+ Lock2(C)
+ END
+ v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value')
+
+ # Lock an object.
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval: list<number>
+ endclass
+
+ def Lock2(sval: any)
+ lockvar sval
+ enddef
+
+ Lock2(C.new())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # In this case (unlike previous) "lockvar sval" is a class member.
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public static var sval: list<number>
+ def Lock2()
+ lockvar sval
+ enddef
+ endclass
+
+
+ var o = C.new()
+ o.Lock2()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1)
+enddef
+
+" Test that this can be locked without error
+def Test_lockvar_this()
+ # lockvar this
+ var lines =<< trim END
+ mnv9script
+ class C
+ def TLock()
+ lockvar this
+ enddef
+ endclass
+ var o = C.new()
+ o.TLock()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # lockvar four (four letter word, but not this)
+ lines =<< trim END
+ mnv9script
+ class C
+ def TLock4()
+ var four: number
+ lockvar four
+ enddef
+ endclass
+ var o = C.new()
+ o.TLock4()
+ END
+ v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
+
+ # lockvar this5; "this" + one char, 5 letter word, starting with "this"
+ lines =<< trim END
+ mnv9script
+ class C
+ def TLock5()
+ var this5: number
+ lockvar this5
+ enddef
+ endclass
+ var o = C.new()
+ o.TLock5()
+ END
+ v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
+enddef
+
+" Test some general lockvar cases
+def Test_lockvar_general()
+ # lockvar an object and a class. It does nothing
+ var lines =<< trim END
+ mnv9script
+ class C
+ endclass
+ var o = C.new()
+ lockvar o
+ lockvar C
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Lock a list element that's nested in an object variable from a :def
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var val: list<list<number>> = [ [1], [2], [3] ]
+ endclass
+ def Lock2(obj: any)
+ lockvar obj.val[1]
+ enddef
+
+ var o = C.new()
+ Lock2(o)
+ o.val[0] = [9]
+ assert_equal([ [9], [2], [3] ], o.val)
+ try
+ o.val[1] = [999]
+ call assert_false(true, 'assign should have failed')
+ catch
+ assert_exception('E741:')
+ endtry
+ o.val[2] = [8]
+ assert_equal([ [9], [2], [8] ], o.val)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Lock a list element that's nested in an object variable from scriptlevel
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var val: list<list<number>> = [ [1], [2], [3] ]
+ endclass
+
+ var o = C.new()
+ lockvar o.val[1]
+ o.val[0] = [9]
+ assert_equal([ [9], [2], [3] ], o.val)
+ try
+ o.val[1] = [999]
+ call assert_false(true, 'assign should have failed')
+ catch
+ assert_exception('E741:')
+ endtry
+ o.val[2] = [8]
+ assert_equal([ [9], [2], [8] ], o.val)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # lock a script level variable from an object method
+ lines =<< trim END
+ mnv9script
+
+ class C
+ def Lock()
+ lockvar l
+ enddef
+ endclass
+
+ var l = [1]
+ C.new().Lock()
+ l[0] = 11
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11)
+
+ # lock a list element referenced by a protected object variable
+ # in an object fetched via a script level list
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var _v1: list<list<number>>
+ def Lock()
+ lockvar lc[0]._v1[1]
+ enddef
+ endclass
+
+ var l = [[1], [2], [3]]
+ var o = C.new(l)
+ var lc: list<C> = [ o ]
+
+ o.Lock()
+ l[0] = [22]
+ l[1] = [33]
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16)
+
+ # similar to the previous test, except the locking code is executing
+ # in a class that does not own the protected variable.
+ # Note that the locking code is in a class has a protected variable of
+ # the same name.
+ lines =<< trim END
+ mnv9script
+
+ class C2
+ var _v1: list<list<number>>
+ def Lock(obj: any)
+ lockvar lc[0]._v1[1]
+ enddef
+ endclass
+
+ class C
+ var _v1: list<list<number>>
+ endclass
+
+ var l = [[1], [2], [3]]
+ var o = C.new(l)
+ var lc: list<C> = [ o ]
+
+ var o2 = C2.new()
+ o2.Lock(o)
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"')
+enddef
+
+" Test builtin islocked()
+def Test_lockvar_islocked()
+ # Can't lock class/object variable
+ # Lock class/object variable's value
+ # Lock item of variable's value (a list item)
+ # variable is at index 1 within class/object
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ var o0: list<list<number>> = [ [0], [1], [2]]
+ var o1: list<list<number>> = [[10], [11], [12]]
+ static var c0: list<list<number>> = [[20], [21], [22]]
+ static var c1: list<list<number>> = [[30], [31], [32]]
+ endclass
+
+ def LockIt(arg: any)
+ lockvar arg
+ enddef
+
+ def UnlockIt(arg: any)
+ unlockvar arg
+ enddef
+
+ var obj = C.new()
+ #lockvar obj.o1 # can't lock something you can't write to
+
+ try
+ lockvar obj.o1 # can't lock something you can't write to
+ call assert_false(1, '"lockvar obj.o1" should have failed')
+ catch
+ call assert_exception('E1335:')
+ endtry
+
+ LockIt(obj.o1) # but can lock it's value
+ assert_equal(1, islocked("obj.o1"))
+ assert_equal(1, islocked("obj.o1[0]"))
+ assert_equal(1, islocked("obj.o1[1]"))
+ UnlockIt(obj.o1)
+ assert_equal(0, islocked("obj.o1"))
+ assert_equal(0, islocked("obj.o1[0]"))
+
+ lockvar obj.o1[0]
+ assert_equal(0, islocked("obj.o1"))
+ assert_equal(1, islocked("obj.o1[0]"))
+ assert_equal(0, islocked("obj.o1[1]"))
+ unlockvar obj.o1[0]
+ assert_equal(0, islocked("obj.o1"))
+ assert_equal(0, islocked("obj.o1[0]"))
+
+ # Same thing, but with a static
+
+ try
+ lockvar C.c1 # can't lock something you can't write to
+ call assert_false(1, '"lockvar C.c1" should have failed')
+ catch
+ call assert_exception('E1335:')
+ endtry
+
+ LockIt(C.c1) # but can lock it's value
+ assert_equal(1, islocked("C.c1"))
+ assert_equal(1, islocked("C.c1[0]"))
+ assert_equal(1, islocked("C.c1[1]"))
+ UnlockIt(C.c1)
+ assert_equal(0, islocked("C.c1"))
+ assert_equal(0, islocked("C.c1[0]"))
+
+ lockvar C.c1[0]
+ assert_equal(0, islocked("C.c1"))
+ assert_equal(1, islocked("C.c1[0]"))
+ assert_equal(0, islocked("C.c1[1]"))
+ unlockvar C.c1[0]
+ assert_equal(0, islocked("C.c1"))
+ assert_equal(0, islocked("C.c1[0]"))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Do islocked() from an object method
+ # and then from a class method
+ lines =<< trim END
+ mnv9script
+
+ var l0o0 = [ [0], [1], [2]]
+ var l0o1 = [ [10], [11], [12]]
+ var l0c0 = [[120], [121], [122]]
+ var l0c1 = [[130], [131], [132]]
+
+ class C0
+ var o0: list<list<number>> = l0o0
+ var o1: list<list<number>> = l0o1
+ static var c0: list<list<number>> = l0c0
+ static var c1: list<list<number>> = l0c1
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ static def SIslocked(arg: string): number
+ return islocked(arg)
+ enddef
+ endclass
+
+ var l2o0 = [[20000], [20001], [20002]]
+ var l2o1 = [[20010], [20011], [20012]]
+ var l2c0 = [[20120], [20121], [20122]]
+ var l2c1 = [[20130], [20131], [20132]]
+
+ class C2
+ var o0: list<list<number>> = l2o0
+ var o1: list<list<number>> = l2o1
+ static var c0: list<list<number>> = l2c0
+ static var c1: list<list<number>> = l2c1
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ static def SIslocked(arg: string): number
+ return islocked(arg)
+ enddef
+ endclass
+
+ var obj0 = C0.new()
+ var obj2 = C2.new()
+
+ var l = [ obj0, null_object, obj2 ]
+
+ # lock list, object func access through script var expr
+ assert_equal(0, obj0.Islocked("l[0].o0"))
+ assert_equal(0, obj0.Islocked("l[0].o0[2]"))
+ lockvar l0o0
+ assert_equal(1, obj0.Islocked("l[0].o0"))
+ assert_equal(1, obj0.Islocked("l[0].o0[2]"))
+
+ #echo "check-b" obj2.Islocked("l[1].o1") # NULL OBJECT
+
+ # lock list element, object func access through script var expr
+ lockvar l0o1[1]
+ assert_equal(0, obj0.Islocked("this.o1[0]"))
+ assert_equal(1, obj0.Islocked("this.o1[1]"))
+
+ assert_equal(0, obj0.Islocked("this.o1"))
+ lockvar l0o1
+ assert_equal(1, obj0.Islocked("this.o1"))
+ unlockvar l0o1
+
+ lockvar l0c1[1]
+
+ # static by class name member expr from same class
+ assert_equal(0, obj0.Islocked("C0.c1[0]"))
+ assert_equal(1, obj0.Islocked("C0.c1[1]"))
+ # static by bare name member expr from same class
+ assert_equal(0, obj0.Islocked("c1[0]"))
+ assert_equal(1, obj0.Islocked("c1[1]"))
+
+ # static by class name member expr from other class
+ assert_equal(0, obj2.Islocked("C0.c1[0]"))
+ assert_equal(1, obj2.Islocked("C0.c1[1]"))
+ # static by bare name member expr from other class
+ assert_equal(0, obj2.Islocked("c1[0]"))
+ assert_equal(0, obj2.Islocked("c1[1]"))
+
+
+ # static by bare name in same class
+ assert_equal(0, obj0.Islocked("c0"))
+ lockvar l0c0
+ assert_equal(1, obj0.Islocked("c0"))
+
+ #
+ # similar stuff, but use static method
+ #
+
+ unlockvar l0o0
+
+ # lock list, object func access through script var expr
+ assert_equal(0, C0.SIslocked("l[0].o0"))
+ assert_equal(0, C0.SIslocked("l[0].o0[2]"))
+ lockvar l0o0
+ assert_equal(1, C0.SIslocked("l[0].o0"))
+ assert_equal(1, C0.SIslocked("l[0].o0[2]"))
+
+ unlockvar l0o1
+
+ # can't access "this" from class method
+ try
+ C0.SIslocked("this.o1[0]")
+ call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed')
+ catch
+ call assert_exception('E121: Undefined variable: this')
+ endtry
+
+ lockvar l0c1[1]
+
+ # static by class name member expr from same class
+ assert_equal(0, C0.SIslocked("C0.c1[0]"))
+ assert_equal(1, C0.SIslocked("C0.c1[1]"))
+ # static by bare name member expr from same class
+ assert_equal(0, C0.SIslocked("c1[0]"))
+ assert_equal(1, C0.SIslocked("c1[1]"))
+
+ # static by class name member expr from other class
+ assert_equal(0, C2.SIslocked("C0.c1[0]"))
+ assert_equal(1, C2.SIslocked("C0.c1[1]"))
+ # static by bare name member expr from other class
+ assert_equal(0, C2.SIslocked("c1[0]"))
+ assert_equal(0, C2.SIslocked("c1[1]"))
+
+
+ # static by bare name in same class
+ unlockvar l0c0
+ assert_equal(0, C0.SIslocked("c0"))
+ lockvar l0c0
+ assert_equal(1, C0.SIslocked("c0"))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Check islocked class/object from various places.
+ lines =<< trim END
+ mnv9script
+
+ class C
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ static def SIslocked(arg: string): number
+ return islocked(arg)
+ enddef
+ endclass
+ var obj = C.new()
+
+ # object method
+ assert_equal(0, obj.Islocked("this"))
+ assert_equal(0, obj.Islocked("C"))
+
+ # class method
+ ### assert_equal(0, C.SIslocked("this"))
+ assert_equal(0, C.SIslocked("C"))
+
+ #script level
+ var v: number
+ v = islocked("C")
+ assert_equal(0, v)
+ v = islocked("obj")
+ assert_equal(0, v)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_lockvar_islocked_notfound()
+ # Try non-existent things
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ static def SIslocked(arg: string): number
+ return islocked(arg)
+ enddef
+ endclass
+ var obj = C.new()
+ assert_equal(-1, obj.Islocked("anywhere"))
+ assert_equal(-1, C.SIslocked("notanywhere"))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Something not found of the form "name1.name2" is an error
+ lines =<< trim END
+ mnv9script
+
+ islocked("one.two")
+ END
+ v9.CheckSourceFailure(lines, 'E121: Undefined variable: one')
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ var val = { key: "value" }
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ endclass
+ var obj = C.new()
+ obj.Islocked("this.val.not_there"))
+ END
+ v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"')
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ endclass
+ var obj = C.new()
+ obj.Islocked("this.notobjmember")
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"')
+
+ # access a script variable through methods
+ lines =<< trim END
+ mnv9script
+
+ var l = [1]
+ class C
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ static def SIslocked(arg: string): number
+ return islocked(arg)
+ enddef
+ endclass
+ var obj = C.new()
+ assert_equal(0, obj.Islocked("l"))
+ assert_equal(0, C.SIslocked("l"))
+ lockvar l
+ assert_equal(1, obj.Islocked("l"))
+ assert_equal(1, C.SIslocked("l"))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for a protected object method
+def Test_protected_object_method()
+ # Try calling a protected method using an object (at the script level)
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ var a = A.new()
+ a._Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)
+
+ # Try calling a protected method using an object (from a def function)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ def T()
+ var a = A.new()
+ a._Foo()
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)
+
+ # Use a protected method from another object method (in script context)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ def Bar(): number
+ return this._Foo()
+ enddef
+ endclass
+ var a = A.new()
+ assert_equal(1234, a.Bar())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use a protected method from another object method (def function context)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ def Bar(): number
+ return this._Foo()
+ enddef
+ endclass
+ def T()
+ var a = A.new()
+ assert_equal(1234, a.Bar())
+ enddef
+ T()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Try calling a protected method without the "this" prefix
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ def Bar(): number
+ return _Foo()
+ enddef
+ endclass
+ var a = A.new()
+ a.Bar()
+ END
+ v9.CheckSourceFailure(lines, 'E117: Unknown function: _Foo', 1)
+
+ # Try calling a protected method using the class name
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ A._Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)
+
+ # Define two protected methods with the same name
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo()
+ enddef
+ def _Foo()
+ enddef
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)
+
+ # Define a protected method and a object method with the same name
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo()
+ enddef
+ def Foo()
+ enddef
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)
+
+ # Define an object method and a protected method with the same name
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def Foo()
+ enddef
+ def _Foo()
+ enddef
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)
+
+ # Call a public method and a protected method from a protected method
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def Foo(): number
+ return 100
+ enddef
+ def _Bar(): number
+ return 200
+ enddef
+ def _Baz()
+ assert_equal(100, this.Foo())
+ assert_equal(200, this._Bar())
+ enddef
+ def T()
+ this._Baz()
+ enddef
+ endclass
+ var a = A.new()
+ a.T()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Try calling a protected method from another class
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def _Foo(): number
+ return 100
+ enddef
+ endclass
+ class B
+ def Foo(): number
+ var a = A.new()
+ a._Foo()
+ enddef
+ endclass
+ var b = B.new()
+ b.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)
+
+ # Call a protected object method from a child class object method
+ lines =<< trim END
+ mnv9script
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ class B extends A
+ def Bar()
+ enddef
+ endclass
+ class C extends B
+ def Baz(): number
+ return this._Foo()
+ enddef
+ endclass
+ var c = C.new()
+ assert_equal(1234, c.Baz())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Call a protected object method from a child class object
+ lines =<< trim END
+ mnv9script
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ class B extends A
+ def Bar()
+ enddef
+ endclass
+ class C extends B
+ def Baz(): number
+ enddef
+ endclass
+ var c = C.new()
+ assert_equal(1234, c._Foo())
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 16)
+
+ # Using "_" prefix in a method name should fail outside of a class
+ lines =<< trim END
+ mnv9script
+ def _Foo(): number
+ return 1234
+ enddef
+ var a = _Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number', 2)
+
+ # Test for initializing a protected funcref instance variable to a protected
+ # class method from another class
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Internal(): string
+ enddef
+ endclass
+
+ class B
+ var Fn: func = A._Internal
+ endclass
+ var b = B.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 1)
+enddef
+
+" Test for an protected class method
+def Test_protected_class_method()
+ # Try calling a class protected method (at the script level)
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ A._Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)
+
+ # Try calling a class protected method (from a def function)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ def T()
+ A._Foo()
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
+
+ # Try calling a class protected method using an object (at the script level)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ var a = A.new()
+ a._Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)
+
+ # Try calling a class protected method using an object (from a def function)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ def T()
+ var a = A.new()
+ a._Foo()
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2)
+
+ # Use a class protected method from an object method
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ def Bar()
+ assert_equal(1234, _Foo())
+ enddef
+ endclass
+ var a = A.new()
+ a.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use a class protected method from another class protected method without the
+ # class name prefix.
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Foo1(): number
+ return 1234
+ enddef
+ static def _Foo2()
+ assert_equal(1234, _Foo1())
+ enddef
+ def Bar()
+ _Foo2()
+ enddef
+ endclass
+ var a = A.new()
+ a.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Declare a class method and a class protected method with the same name
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Foo()
+ enddef
+ static def Foo()
+ enddef
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)
+
+ # Try calling a class protected method from another class
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ class B
+ def Foo(): number
+ return A._Foo()
+ enddef
+ endclass
+ var b = B.new()
+ assert_equal(1234, b.Foo())
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
+
+ # Call a protected class method from a child class object method
+ lines =<< trim END
+ mnv9script
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ class B extends A
+ def Bar()
+ enddef
+ endclass
+ class C extends B
+ def Baz(): number
+ return A._Foo()
+ enddef
+ endclass
+ var c = C.new()
+ assert_equal(1234, c.Baz())
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
+
+ # Call a protected class method from a child class protected class method
+ lines =<< trim END
+ mnv9script
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ class B extends A
+ def Bar()
+ enddef
+ endclass
+ class C extends B
+ static def Baz(): number
+ return A._Foo()
+ enddef
+ endclass
+ assert_equal(1234, C.Baz())
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
+
+ # Call a protected class method from a child class object
+ lines =<< trim END
+ mnv9script
+ class A
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ class B extends A
+ def Bar()
+ enddef
+ endclass
+ class C extends B
+ def Baz(): number
+ enddef
+ endclass
+ var c = C.new()
+ assert_equal(1234, C._Foo())
+ END
+ v9.CheckSourceFailure(lines, 'E1325: Method "_Foo" not found in class "C"', 16)
+
+ # Test for initializing a protected funcref class variable to a protected
+ # class method from another class
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static def _Internal(): string
+ enddef
+ endclass
+
+ class B
+ static var Fn: func = A._Internal
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 10)
+enddef
+
+" Test for using the return value of a class/object method as a function
+" argument.
+def Test_objmethod_funcarg()
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ def Foo(): string
+ return 'foo'
+ enddef
+ endclass
+
+ def Bar(a: number, s: string): string
+ return s
+ enddef
+
+ def Baz(c: C)
+ assert_equal('foo', Bar(10, c.Foo()))
+ enddef
+
+ var t = C.new()
+ Baz(t)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ static def Foo(): string
+ return 'foo'
+ enddef
+ endclass
+
+ def Bar(a: number, s: string): string
+ return s
+ enddef
+
+ def Baz()
+ assert_equal('foo', Bar(10, C.Foo()))
+ enddef
+
+ Baz()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_static_inheritence()
+ # subclasses get their own static copy
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ static var _svar: number
+ var _mvar: number
+ def new()
+ _svar = 1
+ this._mvar = 101
+ enddef
+ def AccessObject(): number
+ return this._mvar
+ enddef
+ def AccessStaticThroughObject(): number
+ return _svar
+ enddef
+ endclass
+
+ class B extends A
+ def new()
+ this._mvar = 102
+ enddef
+ endclass
+
+ class C extends B
+ def new()
+ this._mvar = 103
+ enddef
+
+ def AccessPrivateStaticThroughClassName(): number
+ assert_equal(1, A._svar)
+ return 444
+ enddef
+ endclass
+
+ var oa = A.new()
+ var ob = B.new()
+ var oc = C.new()
+ assert_equal(101, oa.AccessObject())
+ assert_equal(102, ob.AccessObject())
+ assert_equal(103, oc.AccessObject())
+
+ assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access protected variable "_svar" in class "A"')
+
+ # verify object properly resolves to correct static
+ assert_equal(1, oa.AccessStaticThroughObject())
+ assert_equal(1, ob.AccessStaticThroughObject())
+ assert_equal(1, oc.AccessStaticThroughObject())
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for declaring duplicate object and class members
+def Test_dup_member_variable()
+ # Duplicate member variable
+ var lines =<< trim END
+ mnv9script
+ class C
+ var val = 10
+ var val = 20
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+
+ # Duplicate protected member variable
+ lines =<< trim END
+ mnv9script
+ class C
+ var _val = 10
+ var _val = 20
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)
+
+ # Duplicate public member variable
+ lines =<< trim END
+ mnv9script
+ class C
+ public var val = 10
+ public var val = 20
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+
+ # Duplicate protected member variable
+ lines =<< trim END
+ mnv9script
+ class C
+ var val = 10
+ var _val = 20
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)
+
+ # Duplicate public and protected member variable
+ lines =<< trim END
+ mnv9script
+ class C
+ var _val = 20
+ public var val = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+
+ # Duplicate class member variable
+ lines =<< trim END
+ mnv9script
+ class C
+ static var s: string = "abc"
+ static var _s: string = "def"
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)
+
+ # Duplicate public and protected class member variable
+ lines =<< trim END
+ mnv9script
+ class C
+ public static var s: string = "abc"
+ static var _s: string = "def"
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)
+
+ # Duplicate class and object member variable
+ lines =<< trim END
+ mnv9script
+ class C
+ static var val = 10
+ var val = 20
+ def new()
+ enddef
+ endclass
+ var c = C.new()
+ assert_equal(10, C.val)
+ assert_equal(20, c.val)
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+
+ # Duplicate object member variable in a derived class
+ lines =<< trim END
+ mnv9script
+ class A
+ var val = 10
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ var val = 20
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)
+
+ # Duplicate object protected member variable in a derived class
+ lines =<< trim END
+ mnv9script
+ class A
+ var _val = 10
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ var _val = 20
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)
+
+ # Duplicate object protected member variable in a derived class
+ lines =<< trim END
+ mnv9script
+ class A
+ var val = 10
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ var _val = 20
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)
+
+ # Duplicate object member variable in a derived class
+ lines =<< trim END
+ mnv9script
+ class A
+ var _val = 10
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ var val = 20
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)
+
+ # Two member variables with a common prefix
+ lines =<< trim END
+ mnv9script
+ class A
+ public static var svar2: number
+ public static var svar: number
+ endclass
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for accessing a protected member outside a class in a def function
+def Test_protected_member_access_outside_class()
+ # try to modify a protected instance variable from a def function
+ var lines =<< trim END
+ mnv9script
+ class A
+ var _val = 10
+ endclass
+ def T()
+ var a = A.new()
+ a._val = 20
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 2)
+
+ # access a non-existing protected instance variable from a def function
+ lines =<< trim END
+ mnv9script
+ class A
+ var _val = 10
+ endclass
+ def T()
+ var a = A.new()
+ a._a = 1
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "_a" not found in object "A"', 2)
+
+ # try to read a protected class variable from a def function using an instance
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _val = 10
+ endclass
+ def T()
+ var a = A.new()
+ var x = a._val
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)
+
+ # try to modify a protected class variable from a def function using an
+ # instance
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _val = 10
+ endclass
+ def T()
+ var a = A.new()
+ a._val = 3
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)
+
+ # try to read a protected class variable from a def function using the class
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _val = 10
+ endclass
+ def T()
+ var x = A._val
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
+
+ # try to modify a protected class variable from a def function using the class
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _val = 10
+ endclass
+ def T()
+ A._val = 3
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
+
+ # initialize a protected class variable using a protected class variable
+ # from another class
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _aval = 10
+ endclass
+ class B
+ static var _bval = A._aval
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 7)
+
+ # initialize a protected instance variable using a protected class variable
+ # from another class
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _aval = 10
+ endclass
+ class B
+ var _bval = A._aval
+ endclass
+ var b = B.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 1)
+enddef
+
+" Test for trying to change a readonly member from a def function
+def Test_readonly_member_change_in_def_func()
+ var lines =<< trim END
+ mnv9script
+ class A
+ var val: number
+ endclass
+ def T()
+ var a = A.new()
+ a.val = 20
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "val" in class "A" is not writable', 2)
+enddef
+
+" Test for reading and writing a class member from a def function
+def Test_modify_class_member_from_def_function()
+ var lines =<< trim END
+ mnv9script
+ class A
+ var var1: number = 10
+ public static var var2: list<number> = [1, 2]
+ public static var var3: dict<number> = {a: 1, b: 2}
+ static var _priv_var4: number = 40
+ endclass
+ def T()
+ assert_equal([1, 2], A.var2)
+ assert_equal({a: 1, b: 2}, A.var3)
+ A.var2 = [3, 4]
+ A.var3 = {c: 3, d: 4}
+ assert_equal([3, 4], A.var2)
+ assert_equal({c: 3, d: 4}, A.var3)
+ assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"')
+ enddef
+ T()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for accessing a class member variable using an object
+def Test_class_variable_access_using_object()
+ var lines =<< trim END
+ mnv9script
+ class A
+ public static var svar1: list<number> = [1]
+ public static var svar2: list<number> = [2]
+ endclass
+
+ A.svar1->add(3)
+ A.svar2->add(4)
+ assert_equal([1, 3], A.svar1)
+ assert_equal([2, 4], A.svar2)
+
+ def Foo()
+ A.svar1->add(7)
+ A.svar2->add(8)
+ assert_equal([1, 3, 7], A.svar1)
+ assert_equal([2, 4, 8], A.svar2)
+ enddef
+ Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Cannot read from a class variable using an object in script context
+ lines =<< trim END
+ mnv9script
+ class A
+ public var var1: number
+ public static var svar2: list<number> = [1]
+ endclass
+
+ var a = A.new()
+ echo a.svar2
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
+
+ # Cannot write to a class variable using an object in script context
+ lines =<< trim END
+ mnv9script
+ class A
+ public var var1: number
+ public static var svar2: list<number> = [1]
+ endclass
+
+ var a = A.new()
+ a.svar2 = [2]
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
+
+ # Cannot read from a class variable using an object in def method context
+ lines =<< trim END
+ mnv9script
+ class A
+ public var var1: number
+ public static var svar2: list<number> = [1]
+ endclass
+
+ def T()
+ var a = A.new()
+ echo a.svar2
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
+
+ # Cannot write to a class variable using an object in def method context
+ lines =<< trim END
+ mnv9script
+ class A
+ public var var1: number
+ public static var svar2: list<number> = [1]
+ endclass
+
+ def T()
+ var a = A.new()
+ a.svar2 = [2]
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
+enddef
+
+" Test for abstract methods
+def Test_abstract_method()
+ # Use two abstract methods
+ var lines =<< trim END
+ mnv9script
+ abstract class A
+ def M1(): number
+ return 10
+ enddef
+ abstract def M2(): number
+ abstract def M3(): number
+ endclass
+ class B extends A
+ def M2(): number
+ return 20
+ enddef
+ def M3(): number
+ return 30
+ enddef
+ endclass
+ var b = B.new()
+ assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()])
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Don't define an abstract method
+ lines =<< trim END
+ mnv9script
+ abstract class A
+ abstract def Foo()
+ endclass
+ class B extends A
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented', 6)
+
+ # Use abstract method in a concrete class
+ lines =<< trim END
+ mnv9script
+ class A
+ abstract def Foo()
+ endclass
+ class B extends A
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class', 3)
+
+ # Use abstract method in an interface
+ lines =<< trim END
+ mnv9script
+ interface A
+ abstract def Foo()
+ endinterface
+ class B implements A
+ def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+
+ # Use abstract static method in an interface
+ lines =<< trim END
+ mnv9script
+ interface A
+ abstract static def Foo()
+ enddef
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+
+ # Use abstract static variable in an interface
+ lines =<< trim END
+ mnv9script
+ interface A
+ abstract static foo: number = 10
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+
+ # Abbreviate the "abstract" keyword
+ lines =<< trim END
+ mnv9script
+ class A
+ abs def Foo()
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3)
+
+ # Use "abstract" with a member variable
+ lines =<< trim END
+ mnv9script
+ abstract class A
+ abstract this.val = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)
+
+ # Use a static abstract method
+ lines =<< trim END
+ mnv9script
+ abstract class A
+ abstract static def Foo(): number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)
+
+ # Type mismatch between abstract method and concrete method
+ lines =<< trim END
+ mnv9script
+ abstract class A
+ abstract def Foo(a: string, b: number): list<number>
+ endclass
+ class B extends A
+ def Foo(a: number, b: string): list<string>
+ return []
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list<number> but got func(number, string): list<string>', 9)
+
+ # Invoke an abstract method from a def function
+ lines =<< trim END
+ mnv9script
+ abstract class A
+ abstract def Foo(): list<number>
+ endclass
+ class B extends A
+ def Foo(): list<number>
+ return [3, 5]
+ enddef
+ endclass
+ def Bar(c: B)
+ assert_equal([3, 5], c.Foo())
+ enddef
+ var b = B.new()
+ Bar(b)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use a static method in an abstract class
+ lines =<< trim END
+ mnv9script
+ abstract class A
+ static def Foo(): string
+ return 'foo'
+ enddef
+ endclass
+ assert_equal('foo', A.Foo())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Invoke method returning a value through the abstract class. See #15432.
+ lines =<< trim END
+ mnv9script
+
+ abstract class A
+ abstract def String(): string
+ endclass
+
+ class B extends A
+ def String(): string
+ return 'B'
+ enddef
+ endclass
+
+ def F(o: A)
+ assert_equal('B', o.String())
+ enddef
+ F(B.new())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Invoke abstract method returning a value does not compile
+ lines =<< trim END
+ mnv9script
+
+ abstract class A
+ abstract def String(): string
+ return 'X'
+ enddef
+ endclass
+ END
+ v9.CheckScriptFailure(lines, "E1318: Not a valid command in a class: return 'X'")
+enddef
+
+" Test for calling a class method from a subclass
+def Test_class_method_call_from_subclass()
+ # class method call from a subclass
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ static def Foo()
+ echo "foo"
+ enddef
+ endclass
+
+ class B extends A
+ def Bar()
+ Foo()
+ enddef
+ endclass
+
+ var b = B.new()
+ b.Bar()
+ END
+ v9.CheckSourceFailure(lines, 'E1384: Class method "Foo" accessible only inside class "A"', 1)
+enddef
+
+" Test for calling a class method using an object in a def function context and
+" script context.
+def Test_class_method_call_using_object()
+ # script context
+ var lines =<< trim END
+ mnv9script
+ class A
+ static def Foo(): list<string>
+ return ['a', 'b']
+ enddef
+ def Bar()
+ assert_equal(['a', 'b'], A.Foo())
+ assert_equal(['a', 'b'], Foo())
+ enddef
+ endclass
+
+ def T()
+ assert_equal(['a', 'b'], A.Foo())
+ var t_a = A.new()
+ t_a.Bar()
+ enddef
+
+ assert_equal(['a', 'b'], A.Foo())
+ var a = A.new()
+ a.Bar()
+ T()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # script context
+ lines =<< trim END
+ mnv9script
+ class A
+ static def Foo(): string
+ return 'foo'
+ enddef
+ endclass
+
+ var a = A.new()
+ assert_equal('foo', a.Foo())
+ END
+ v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 9)
+
+ # def function context
+ lines =<< trim END
+ mnv9script
+ class A
+ static def Foo(): string
+ return 'foo'
+ enddef
+ endclass
+
+ def T()
+ var a = A.new()
+ assert_equal('foo', a.Foo())
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 2)
+enddef
+
+def Test_class_variable()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ public static var val: number = 10
+ static def ClassFunc()
+ assert_equal(10, val)
+ enddef
+ def ObjFunc()
+ assert_equal(10, val)
+ enddef
+ endclass
+
+ class B extends A
+ endclass
+
+ assert_equal(10, A.val)
+ A.ClassFunc()
+ var a = A.new()
+ a.ObjFunc()
+ var b = B.new()
+ b.ObjFunc()
+
+ def T1(a1: A)
+ a1.ObjFunc()
+ A.ClassFunc()
+ enddef
+ T1(b)
+
+ A.val = 20
+ assert_equal(20, A.val)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Modifying a parent class variable from a child class method
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var val: number = 10
+ endclass
+
+ class B extends A
+ static def ClassFunc()
+ val = 20
+ enddef
+ endclass
+ B.ClassFunc()
+ END
+ v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+
+ # Reading a parent class variable from a child class method
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var val: number = 10
+ endclass
+
+ class B extends A
+ static def ClassFunc()
+ var i = val
+ enddef
+ endclass
+ B.ClassFunc()
+ END
+ v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+
+ # Modifying a parent class variable from a child object method
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var val: number = 10
+ endclass
+
+ class B extends A
+ def ObjFunc()
+ val = 20
+ enddef
+ endclass
+ var b = B.new()
+ b.ObjFunc()
+ END
+ v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+
+ # Reading a parent class variable from a child object method
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var val: number = 10
+ endclass
+
+ class B extends A
+ def ObjFunc()
+ var i = val
+ enddef
+ endclass
+ var b = B.new()
+ b.ObjFunc()
+ END
+ v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+
+ # Modifying a class variable using an object at script level
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var val: number = 10
+ endclass
+ var a = A.new()
+ a.val = 20
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
+
+ # Reading a class variable using an object at script level
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var val: number = 10
+ endclass
+ var a = A.new()
+ var i = a.val
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
+
+ # Modifying a class variable using an object at function level
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var val: number = 10
+ endclass
+
+ def T()
+ var a = A.new()
+ a.val = 20
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)
+
+ # Reading a class variable using an object at function level
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var val: number = 10
+ endclass
+ def T()
+ var a = A.new()
+ var i = a.val
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)
+
+ # Use old implicit var declaration syntax (without initialization)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static val: number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+
+ # Use old implicit var declaration syntax (with initialization)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static val: number = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+
+ # Use old implicit var declaration syntax (type inferred)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static val = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+
+ # Missing ":var" in "var" class variable declaration (without initialization)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var: number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number', 4)
+
+ # Missing ":var" in "var" class variable declaration (with initialization)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var: number = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number = 10', 4)
+
+ # Missing ":var" in "var" class variable declaration (type inferred)
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var = 10', 4)
+
+enddef
+
+" Test for using a duplicate class method and class variable in a child class
+def Test_dup_class_member()
+ # duplicate class variable, class method and overridden object method
+ var lines =<< trim END
+ mnv9script
+ class A
+ static var sval = 100
+ static def Check()
+ assert_equal(100, sval)
+ enddef
+ def GetVal(): number
+ return sval
+ enddef
+ endclass
+
+ class B extends A
+ static var sval = 200
+ static def Check()
+ assert_equal(200, sval)
+ enddef
+ def GetVal(): number
+ return sval
+ enddef
+ endclass
+
+ def T1(aa: A): number
+ return aa.GetVal()
+ enddef
+
+ def T2(bb: B): number
+ return bb.GetVal()
+ enddef
+
+ assert_equal(100, A.sval)
+ assert_equal(200, B.sval)
+ var a = A.new()
+ assert_equal(100, a.GetVal())
+ var b = B.new()
+ assert_equal(200, b.GetVal())
+ assert_equal(200, T1(b))
+ assert_equal(200, T2(b))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # duplicate class variable and class method
+ lines =<< trim END
+ mnv9script
+ class A
+ static var sval = 100
+ static def Check()
+ assert_equal(100, sval)
+ enddef
+ def GetVal(): number
+ return sval
+ enddef
+ endclass
+
+ class B extends A
+ static var sval = 200
+ static def Check()
+ assert_equal(200, sval)
+ enddef
+ endclass
+
+ def T1(aa: A): number
+ return aa.GetVal()
+ enddef
+
+ def T2(bb: B): number
+ return bb.GetVal()
+ enddef
+
+ assert_equal(100, A.sval)
+ assert_equal(200, B.sval)
+ var a = A.new()
+ assert_equal(100, a.GetVal())
+ var b = B.new()
+ assert_equal(100, b.GetVal())
+ assert_equal(100, T1(b))
+ assert_equal(100, T2(b))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for calling an instance method using the class
+def Test_instance_method_call_using_class()
+ # Invoke an object method using a class in script context
+ var lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ echo "foo"
+ enddef
+ endclass
+ A.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 7)
+
+ # Invoke an object method using a class in def function context
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ echo "foo"
+ enddef
+ endclass
+ def T()
+ A.Foo()
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 1)
+enddef
+
+" Test for duplicate class method and instance method
+def Test_dup_classmethod_objmethod()
+ # Duplicate instance method
+ var lines =<< trim END
+ mnv9script
+ class A
+ static def Foo()
+ enddef
+ def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)
+
+ # Duplicate protected instance method
+ lines =<< trim END
+ mnv9script
+ class A
+ static def Foo()
+ enddef
+ def _Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+
+ # Duplicate class method
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ enddef
+ static def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)
+
+ # Duplicate protected class method
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ enddef
+ static def _Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+
+ # Duplicate protected class and object method
+ lines =<< trim END
+ mnv9script
+ class A
+ def _Foo()
+ enddef
+ static def _Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+enddef
+
+" Test for an instance method access level comparison with parent instance
+" methods.
+def Test_instance_method_access_level()
+ # protected method in subclass
+ var lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ enddef
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ def _Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"', 11)
+
+ # Public method in subclass
+ lines =<< trim END
+ mnv9script
+ class A
+ def _Foo()
+ enddef
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"', 11)
+enddef
+
+def Test_extend_empty_class()
+ var lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ public static var rw_class_var = 1
+ public var rw_obj_var = 2
+ static def ClassMethod(): number
+ return 3
+ enddef
+ def ObjMethod(): number
+ return 4
+ enddef
+ endclass
+ assert_equal(1, C.rw_class_var)
+ assert_equal(3, C.ClassMethod())
+ var c = C.new()
+ assert_equal(2, c.rw_obj_var)
+ assert_equal(4, c.ObjMethod())
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for assigning to a member variable in a nested class
+def Test_nested_object_assignment()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ var value: number
+ endclass
+
+ class B
+ var a: A = A.new()
+ endclass
+
+ class C
+ var b: B = B.new()
+ endclass
+
+ class D
+ var c: C = C.new()
+ endclass
+
+ def T(da: D)
+ da.c.b.a.value = 10
+ enddef
+
+ var d = D.new()
+ T(d)
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "value" in class "A" is not writable', 1)
+enddef
+
+" Test for calling methods using a null object
+def Test_null_object_method_call()
+ # Calling a object method using a null object in script context
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ def Foo()
+ assert_report('This method should not be executed')
+ enddef
+ endclass
+
+ var o: C
+ o.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 10)
+
+ # Calling a object method using a null object in def function context
+ lines =<< trim END
+ mnv9script
+
+ class C
+ def Foo()
+ assert_report('This method should not be executed')
+ enddef
+ endclass
+
+ def T()
+ var o: C
+ o.Foo()
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+ # Calling a object method through another class method using a null object in
+ # script context
+ lines =<< trim END
+ mnv9script
+
+ class C
+ def Foo()
+ assert_report('This method should not be executed')
+ enddef
+
+ static def Bar(o_any: any)
+ var o_typed: C = o_any
+ o_typed.Foo()
+ enddef
+ endclass
+
+ var o: C
+ C.Bar(o)
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+ # Calling a object method through another class method using a null object in
+ # def function context
+ lines =<< trim END
+ mnv9script
+
+ class C
+ def Foo()
+ assert_report('This method should not be executed')
+ enddef
+
+ static def Bar(o_any: any)
+ var o_typed: C = o_any
+ o_typed.Foo()
+ enddef
+ endclass
+
+ def T()
+ var o: C
+ C.Bar(o)
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+ # Calling an object method defined in a class that is extended. This differs
+ # from the previous by invoking ISN_METHODCALL instead of ISN_DCALL.
+ lines =<< trim END
+ mnv9script
+
+ class C0
+ def F()
+ enddef
+ endclass
+
+ class C extends C0
+ endclass
+
+ def X()
+ var o: C0 = null_object
+ o.F()
+ enddef
+ X()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+ # Getting a function ref an object method.
+ lines =<< trim END
+ mnv9script
+
+ class C0
+ def F()
+ enddef
+ endclass
+
+ class C extends C0
+ endclass
+
+ def X()
+ var o: C0 = null_object
+ var XXX = o.F
+ enddef
+ X()
+ END
+ v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+enddef
+
+" Test for using a dict as an object member
+def Test_dict_object_member()
+ var lines =<< trim END
+ mnv9script
+
+ class Context
+ public var state: dict<number> = {}
+ def GetState(): dict<number>
+ return this.state
+ enddef
+ endclass
+
+ var ctx = Context.new()
+ ctx.state->extend({a: 1})
+ ctx.state['b'] = 2
+ assert_equal({a: 1, b: 2}, ctx.GetState())
+
+ def F()
+ ctx.state['c'] = 3
+ assert_equal({a: 1, b: 2, c: 3}, ctx.GetState())
+ enddef
+ F()
+ assert_equal(3, ctx.state.c)
+ ctx.state.c = 4
+ assert_equal(4, ctx.state.c)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" The following test was failing after 9.0.1914. This was caused by using a
+" freed object from a previous method call.
+def Test_freed_object_from_previous_method_call()
+ var lines =<< trim END
+ mnv9script
+
+ class Context
+ endclass
+
+ class Result
+ endclass
+
+ def Failure(): Result
+ return Result.new()
+ enddef
+
+ def GetResult(ctx: Context): Result
+ return Failure()
+ enddef
+
+ def Test_GetResult()
+ var ctx = Context.new()
+ var result = GetResult(ctx)
+ enddef
+
+ Test_GetResult()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for duplicate object and class variable
+def Test_duplicate_variable()
+ # Object variable name is same as the class variable name
+ var lines =<< trim END
+ mnv9script
+ class A
+ public static var sval: number
+ public var sval: number
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4)
+
+ # Duplicate variable name and calling a class method
+ lines =<< trim END
+ mnv9script
+ class A
+ public static var sval: number
+ public var sval: number
+ def F1()
+ echo this.sval
+ enddef
+ static def F2()
+ echo sval
+ enddef
+ endclass
+ A.F2()
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4)
+
+ # Duplicate variable with an empty constructor
+ lines =<< trim END
+ mnv9script
+ class A
+ public static var sval: number
+ public var sval: number
+ def new()
+ enddef
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4)
+enddef
+
+" Test for using a reserved keyword as a variable name
+def Test_reserved_varname()
+ for kword in ['true', 'false', 'null', 'null_blob', 'null_dict',
+ 'null_function', 'null_list', 'null_partial', 'null_string',
+ 'null_channel', 'null_job', 'super', 'this']
+
+ var lines =<< trim eval END
+ mnv9script
+ class C
+ public var {kword}: list<number> = [1, 2, 3]
+ endclass
+ var o = C.new()
+ END
+ v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+
+ lines =<< trim eval END
+ mnv9script
+ class C
+ public var {kword}: list<number> = [1, 2, 3]
+ def new()
+ enddef
+ endclass
+ var o = C.new()
+ END
+ v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+
+ lines =<< trim eval END
+ mnv9script
+ class C
+ public var {kword}: list<number> = [1, 2, 3]
+ def new()
+ enddef
+ def F()
+ echo this.{kword}
+ enddef
+ endclass
+ var o = C.new()
+ o.F()
+ END
+ v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+
+ # class variable name
+ if kword != 'this'
+ lines =<< trim eval END
+ mnv9script
+ class C
+ public static var {kword}: list<number> = [1, 2, 3]
+ endclass
+ END
+ v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+ endif
+ endfor
+enddef
+
+" Test for checking the type of the arguments and the return value of a object
+" method in an extended class.
+def Test_extended_obj_method_type_check()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ endclass
+
+ class Foo
+ def Doit(p: B): B
+ return B.new()
+ enddef
+ endclass
+
+ class Bar extends Foo
+ def Doit(p: C): B
+ return B.new()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object<B>): object<B> but got func(object<C>): object<B>', 20)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ endclass
+
+ class Foo
+ def Doit(p: B): B
+ return B.new()
+ enddef
+ endclass
+
+ class Bar extends Foo
+ def Doit(p: B): C
+ return C.new()
+ enddef
+ endclass
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ endclass
+
+ class Foo
+ def Doit(p: B): B
+ return B.new()
+ enddef
+ endclass
+
+ class Bar extends Foo
+ def Doit(p: A): B
+ return B.new()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object<B>): object<B> but got func(object<A>): object<B>', 20)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ endclass
+
+ class Foo
+ def Doit(p: B): B
+ return B.new()
+ enddef
+ endclass
+
+ class Bar extends Foo
+ def Doit(p: B): A
+ return A.new()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object<B>): object<B> but got func(object<B>): object<A>', 20)
+
+ # check varargs type mismatch
+ lines =<< trim END
+ mnv9script
+
+ class B
+ def F(...xxx: list<any>)
+ enddef
+ endclass
+ class C extends B
+ def F(xxx: list<any>)
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "F": type mismatch, expected func(...list<any>) but got func(list<any>)', 10)
+enddef
+
+" Test type checking for class variable in assignments
+func Test_class_variable_complex_type_check()
+ " class variable with a specific type. Try assigning a different type at
+ " script level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo
+ endclass
+ test_garbagecollect_now()
+ A.Fn = "abc"
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 9)
+
+ " class variable with a specific type. Try assigning a different type at
+ " class def method level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo
+ def Bar()
+ Fn = "abc"
+ enddef
+ endclass
+ var a = A.new()
+ test_garbagecollect_now()
+ a.Bar()
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 1)
+
+ " class variable with a specific type. Try assigning a different type at
+ " script def method level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo
+ endclass
+ def Bar()
+ A.Fn = "abc"
+ enddef
+ test_garbagecollect_now()
+ Bar()
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 1)
+
+ " class variable without any type. Should be set to the initialization
+ " expression type. Try assigning a different type from script level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn = Foo
+ endclass
+ test_garbagecollect_now()
+ A.Fn = "abc"
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 9)
+
+ " class variable without any type. Should be set to the initialization
+ " expression type. Try assigning a different type at class def level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn = Foo
+ def Bar()
+ Fn = "abc"
+ enddef
+ endclass
+ var a = A.new()
+ test_garbagecollect_now()
+ a.Bar()
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 1)
+
+ " class variable without any type. Should be set to the initialization
+ " expression type. Try assigning a different type at script def level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn = Foo
+ endclass
+ def Bar()
+ A.Fn = "abc"
+ enddef
+ test_garbagecollect_now()
+ Bar()
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 1)
+
+ " class variable with 'any" type. Can be assigned different types.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn: any = Foo
+ public static var Fn2: any
+ endclass
+ test_garbagecollect_now()
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(A.Fn))
+ A.Fn = "abc"
+ test_garbagecollect_now()
+ assert_equal('string', typename(A.Fn))
+ A.Fn2 = Foo
+ test_garbagecollect_now()
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(A.Fn2))
+ A.Fn2 = "xyz"
+ test_garbagecollect_now()
+ assert_equal('string', typename(A.Fn2))
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " class variable with 'any" type. Can be assigned different types.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn: any = Foo
+ public static var Fn2: any
+
+ def Bar()
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(Fn))
+ Fn = "abc"
+ assert_equal('string', typename(Fn))
+ Fn2 = Foo
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(Fn2))
+ Fn2 = "xyz"
+ assert_equal('string', typename(Fn2))
+ enddef
+ endclass
+ var a = A.new()
+ test_garbagecollect_now()
+ a.Bar()
+ test_garbagecollect_now()
+ A.Fn = Foo
+ a.Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " class variable with 'any" type. Can be assigned different types.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public static var Fn: any = Foo
+ public static var Fn2: any
+ endclass
+
+ def Bar()
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(A.Fn))
+ A.Fn = "abc"
+ assert_equal('string', typename(A.Fn))
+ A.Fn2 = Foo
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(A.Fn2))
+ A.Fn2 = "xyz"
+ assert_equal('string', typename(A.Fn2))
+ enddef
+ Bar()
+ test_garbagecollect_now()
+ A.Fn = Foo
+ Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ let lines =<< trim END
+ mnv9script
+ class A
+ public static var foo = [0z10, 0z20]
+ endclass
+ assert_equal([0z10, 0z20], A.foo)
+ A.foo = [0z30]
+ assert_equal([0z30], A.foo)
+ var a = A.foo
+ assert_equal([0z30], a)
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test type checking for object variable in assignments
+func Test_object_variable_complex_type_check()
+ " object variable with a specific type. Try assigning a different type at
+ " script level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo
+ endclass
+ var a = A.new()
+ test_garbagecollect_now()
+ a.Fn = "abc"
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 10)
+
+ " object variable with a specific type. Try assigning a different type at
+ " object def method level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo
+ def Bar()
+ this.Fn = "abc"
+ this.Fn = Foo
+ enddef
+ endclass
+ var a = A.new()
+ test_garbagecollect_now()
+ a.Bar()
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 1)
+
+ " object variable with a specific type. Try assigning a different type at
+ " script def method level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo
+ endclass
+ def Bar()
+ var a = A.new()
+ a.Fn = "abc"
+ a.Fn = Foo
+ enddef
+ test_garbagecollect_now()
+ Bar()
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 2)
+
+ " object variable without any type. Should be set to the initialization
+ " expression type. Try assigning a different type from script level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn = Foo
+ endclass
+ var a = A.new()
+ test_garbagecollect_now()
+ a.Fn = "abc"
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 10)
+
+ " object variable without any type. Should be set to the initialization
+ " expression type. Try assigning a different type at object def level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn = Foo
+ def Bar()
+ this.Fn = "abc"
+ this.Fn = Foo
+ enddef
+ endclass
+ var a = A.new()
+ test_garbagecollect_now()
+ a.Bar()
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 1)
+
+ " object variable without any type. Should be set to the initialization
+ " expression type. Try assigning a different type at script def level.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn = Foo
+ endclass
+ def Bar()
+ var a = A.new()
+ a.Fn = "abc"
+ a.Fn = Foo
+ enddef
+ test_garbagecollect_now()
+ Bar()
+ END
+ call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> but got string', 2)
+
+ " object variable with 'any" type. Can be assigned different types.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn: any = Foo
+ public var Fn2: any
+ endclass
+
+ var a = A.new()
+ test_garbagecollect_now()
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(a.Fn))
+ a.Fn = "abc"
+ test_garbagecollect_now()
+ assert_equal('string', typename(a.Fn))
+ a.Fn2 = Foo
+ test_garbagecollect_now()
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(a.Fn2))
+ a.Fn2 = "xyz"
+ test_garbagecollect_now()
+ assert_equal('string', typename(a.Fn2))
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " object variable with 'any" type. Can be assigned different types.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn: any = Foo
+ public var Fn2: any
+
+ def Bar()
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(this.Fn))
+ this.Fn = "abc"
+ assert_equal('string', typename(this.Fn))
+ this.Fn2 = Foo
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(this.Fn2))
+ this.Fn2 = "xyz"
+ assert_equal('string', typename(this.Fn2))
+ enddef
+ endclass
+
+ var a = A.new()
+ test_garbagecollect_now()
+ a.Bar()
+ test_garbagecollect_now()
+ a.Fn = Foo
+ a.Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " object variable with 'any" type. Can be assigned different types.
+ let lines =<< trim END
+ mnv9script
+ def Foo(l: list<dict<blob>>): dict<list<blob>>
+ return {}
+ enddef
+ class A
+ public var Fn: any = Foo
+ public var Fn2: any
+ endclass
+
+ def Bar()
+ var a = A.new()
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(a.Fn))
+ a.Fn = "abc"
+ assert_equal('string', typename(a.Fn))
+ a.Fn2 = Foo
+ assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(a.Fn2))
+ a.Fn2 = "xyz"
+ assert_equal('string', typename(a.Fn2))
+ enddef
+ test_garbagecollect_now()
+ Bar()
+ test_garbagecollect_now()
+ Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for recursively calling an object method. This used to cause an
+" use-after-free error.
+def Test_recursive_object_method_call()
+ var lines =<< trim END
+ mnv9script
+ class A
+ var val: number = 0
+ def Foo(): number
+ if this.val >= 90
+ return this.val
+ endif
+ this.val += 1
+ return this.Foo()
+ enddef
+ endclass
+ var a = A.new()
+ assert_equal(90, a.Foo())
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for recursively calling a class method.
+def Test_recursive_class_method_call()
+ var lines =<< trim END
+ mnv9script
+ class A
+ static var val: number = 0
+ static def Foo(): number
+ if val >= 90
+ return val
+ endif
+ val += 1
+ return Foo()
+ enddef
+ endclass
+ assert_equal(90, A.Foo())
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for checking the argument types and the return type when assigning a
+" funcref to make sure the invariant class type is used.
+def Test_funcref_argtype_returntype_check()
+ var lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B extends A
+ endclass
+
+ def Foo(p: B): B
+ return B.new()
+ enddef
+
+ var Bar: func(A): A = Foo
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object<A>): object<A> but got func(object<B>): object<B>', 11)
+
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B extends A
+ endclass
+
+ def Foo(p: B): B
+ return B.new()
+ enddef
+
+ def Baz()
+ var Bar: func(A): A = Foo
+ enddef
+ Baz()
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object<A>): object<A> but got func(object<B>): object<B>', 1)
+enddef
+
+def Test_funcref_argtype_invariance_check()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ endclass
+
+ var Func: func(B): number
+ Func = (o: B): number => 3
+ assert_equal(3, Func(B.new()))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ endclass
+
+ var Func: func(B): number
+ Func = (o: A): number => 3
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object<B>): number but got func(object<A>): number', 11)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ endclass
+
+ var Func: func(B): number
+ Func = (o: C): number => 3
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object<B>): number but got func(object<C>): number', 11)
+enddef
+
+" Test for using an operator (e.g. +) with an assignment
+def Test_op_and_assignment()
+ # Using += with a class variable
+ var lines =<< trim END
+ mnv9script
+ class A
+ public static var val: list<number> = []
+ static def Foo(): list<number>
+ val += [1]
+ return val
+ enddef
+ endclass
+ def Bar(): list<number>
+ A.val += [2]
+ return A.val
+ enddef
+ assert_equal([1], A.Foo())
+ assert_equal([1, 2], Bar())
+ A.val += [3]
+ assert_equal([1, 2, 3], A.val)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using += with an object variable
+ lines =<< trim END
+ mnv9script
+ class A
+ public var val: list<number> = []
+ def Foo(): list<number>
+ this.val += [1]
+ return this.val
+ enddef
+ endclass
+ def Bar(bar_a: A): list<number>
+ bar_a.val += [2]
+ return bar_a.val
+ enddef
+ var a = A.new()
+ assert_equal([1], a.Foo())
+ assert_equal([1, 2], Bar(a))
+ a.val += [3]
+ assert_equal([1, 2, 3], a.val)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an object method as a funcref
+def Test_object_funcref()
+ # Using object method funcref from a def function
+ var lines =<< trim END
+ mnv9script
+ class A
+ def Foo(): list<number>
+ return [3, 2, 1]
+ enddef
+ endclass
+ def Bar()
+ var a = A.new()
+ var Fn = a.Foo
+ assert_equal([3, 2, 1], Fn())
+ enddef
+ Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using object method funcref at the script level
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo(): dict<number>
+ return {a: 1, b: 2}
+ enddef
+ endclass
+ var a = A.new()
+ var Fn = a.Foo
+ assert_equal({a: 1, b: 2}, Fn())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using object method funcref at the script level
+ lines =<< trim END
+ mnv9script
+ class A
+ var val: number
+ def Foo(): number
+ return this.val
+ enddef
+ endclass
+ var a = A.new(345)
+ var Fn = a.Foo
+ assert_equal(345, Fn())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using object method funcref from another object method
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo(): list<number>
+ return [3, 2, 1]
+ enddef
+ def Bar()
+ var Fn = this.Foo
+ assert_equal([3, 2, 1], Fn())
+ enddef
+ endclass
+ var a = A.new()
+ a.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using function() to get a object method funcref
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo(l: list<any>): list<any>
+ return l
+ enddef
+ endclass
+ var a = A.new()
+ var Fn = function(a.Foo, [[{a: 1, b: 2}, [3, 4]]])
+ assert_equal([{a: 1, b: 2}, [3, 4]], Fn())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use an object method with a function returning a funcref and then call the
+ # funcref.
+ lines =<< trim END
+ mnv9script
+
+ def Map(F: func(number): number): func(number): number
+ return (n: number) => F(n)
+ enddef
+
+ class Math
+ def Double(n: number): number
+ return 2 * n
+ enddef
+ endclass
+
+ const math = Math.new()
+ assert_equal(48, Map(math.Double)(24))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Try using a protected object method funcref from a def function
+ lines =<< trim END
+ mnv9script
+ class A
+ def _Foo()
+ enddef
+ endclass
+ def Bar()
+ var a = A.new()
+ var Fn = a._Foo
+ enddef
+ Bar()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2)
+
+ # Try using a protected object method funcref at the script level
+ lines =<< trim END
+ mnv9script
+ class A
+ def _Foo()
+ enddef
+ endclass
+ var a = A.new()
+ var Fn = a._Foo
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 7)
+
+ # Using a protected object method funcref from another object method
+ lines =<< trim END
+ mnv9script
+ class A
+ def _Foo(): list<number>
+ return [3, 2, 1]
+ enddef
+ def Bar()
+ var Fn = this._Foo
+ assert_equal([3, 2, 1], Fn())
+ enddef
+ endclass
+ var a = A.new()
+ a.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using object method funcref using call()
+ lines =<< trim END
+ mnv9script
+ class A
+ var val: number
+ def Foo(): number
+ return this.val
+ enddef
+ endclass
+
+ def Bar(obj: A)
+ assert_equal(123, call(obj.Foo, []))
+ enddef
+
+ var a = A.new(123)
+ Bar(a)
+ assert_equal(123, call(a.Foo, []))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a class method as a funcref
+def Test_class_funcref()
+ # Using class method funcref in a def function
+ var lines =<< trim END
+ mnv9script
+ class A
+ static def Foo(): list<number>
+ return [3, 2, 1]
+ enddef
+ endclass
+ def Bar()
+ var Fn = A.Foo
+ assert_equal([3, 2, 1], Fn())
+ enddef
+ Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using class method funcref at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ static def Foo(): dict<number>
+ return {a: 1, b: 2}
+ enddef
+ endclass
+ var Fn = A.Foo
+ assert_equal({a: 1, b: 2}, Fn())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using class method funcref at the script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public static var val: number
+ static def Foo(): number
+ return val
+ enddef
+ endclass
+ A.val = 567
+ var Fn = A.Foo
+ assert_equal(567, Fn())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using function() to get a class method funcref
+ lines =<< trim END
+ mnv9script
+ class A
+ static def Foo(l: list<any>): list<any>
+ return l
+ enddef
+ endclass
+ var Fn = function(A.Foo, [[{a: 1, b: 2}, [3, 4]]])
+ assert_equal([{a: 1, b: 2}, [3, 4]], Fn())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a class method funcref from another class method
+ lines =<< trim END
+ mnv9script
+ class A
+ static def Foo(): list<number>
+ return [3, 2, 1]
+ enddef
+ static def Bar()
+ var Fn = Foo
+ assert_equal([3, 2, 1], Fn())
+ enddef
+ endclass
+ A.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use a class method with a function returning a funcref and then call the
+ # funcref.
+ lines =<< trim END
+ mnv9script
+
+ def Map(F: func(number): number): func(number): number
+ return (n: number) => F(n)
+ enddef
+
+ class Math
+ static def StaticDouble(n: number): number
+ return 2 * n
+ enddef
+ endclass
+
+ assert_equal(48, Map(Math.StaticDouble)(24))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Try using a protected class method funcref in a def function
+ lines =<< trim END
+ mnv9script
+ class A
+ static def _Foo()
+ enddef
+ endclass
+ def Bar()
+ var Fn = A._Foo
+ enddef
+ Bar()
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 1)
+
+ # Try using a protected class method funcref at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ static def _Foo()
+ enddef
+ endclass
+ var Fn = A._Foo
+ END
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 6)
+
+ # Using a protected class method funcref from another class method
+ lines =<< trim END
+ mnv9script
+ class A
+ static def _Foo(): list<number>
+ return [3, 2, 1]
+ enddef
+ static def Bar()
+ var Fn = _Foo
+ assert_equal([3, 2, 1], Fn())
+ enddef
+ endclass
+ A.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using class method funcref using call()
+ lines =<< trim END
+ mnv9script
+ class A
+ public static var val: number
+ static def Foo(): number
+ return val
+ enddef
+ endclass
+
+ def Bar()
+ A.val = 468
+ assert_equal(468, call(A.Foo, []))
+ enddef
+ Bar()
+ assert_equal(468, call(A.Foo, []))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an object member as a funcref
+def Test_object_member_funcref()
+ # Using a funcref object variable in an object method
+ var lines =<< trim END
+ mnv9script
+ def Foo(n: number): number
+ return n * 10
+ enddef
+
+ class A
+ var Cb: func(number): number = Foo
+ def Bar()
+ assert_equal(200, this.Cb(20))
+ enddef
+ endclass
+
+ var a = A.new()
+ a.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref object variable in a def method
+ lines =<< trim END
+ mnv9script
+ def Foo(n: number): number
+ return n * 10
+ enddef
+
+ class A
+ var Cb: func(number): number = Foo
+ endclass
+
+ def Bar()
+ var a = A.new()
+ assert_equal(200, a.Cb(20))
+ enddef
+ Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref object variable at script level
+ lines =<< trim END
+ mnv9script
+ def Foo(n: number): number
+ return n * 10
+ enddef
+
+ class A
+ var Cb: func(number): number = Foo
+ endclass
+
+ var a = A.new()
+ assert_equal(200, a.Cb(20))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref object variable pointing to an object method in an object
+ # method.
+ lines =<< trim END
+ mnv9script
+ class A
+ var Cb: func(number): number = this.Foo
+ def Foo(n: number): number
+ return n * 10
+ enddef
+ def Bar()
+ assert_equal(200, this.Cb(20))
+ enddef
+ endclass
+
+ var a = A.new()
+ a.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref object variable pointing to an object method in a def
+ # method.
+ lines =<< trim END
+ mnv9script
+ class A
+ var Cb: func(number): number = this.Foo
+ def Foo(n: number): number
+ return n * 10
+ enddef
+ endclass
+
+ def Bar()
+ var a = A.new()
+ assert_equal(200, a.Cb(20))
+ enddef
+ Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref object variable pointing to an object method at script
+ # level.
+ lines =<< trim END
+ mnv9script
+ class A
+ var Cb = this.Foo
+ def Foo(n: number): number
+ return n * 10
+ enddef
+ endclass
+
+ var a = A.new()
+ assert_equal(200, a.Cb(20))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a class member as a funcref
+def Test_class_member_funcref()
+ # Using a funcref class variable in a class method
+ var lines =<< trim END
+ mnv9script
+ def Foo(n: number): number
+ return n * 10
+ enddef
+
+ class A
+ static var Cb = Foo
+ static def Bar()
+ assert_equal(200, Cb(20))
+ enddef
+ endclass
+
+ A.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref class variable in a def method
+ lines =<< trim END
+ mnv9script
+ def Foo(n: number): number
+ return n * 10
+ enddef
+
+ class A
+ public static var Cb = Foo
+ endclass
+
+ def Bar()
+ assert_equal(200, A.Cb(20))
+ enddef
+ Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref class variable at script level
+ lines =<< trim END
+ mnv9script
+ def Foo(n: number): number
+ return n * 10
+ enddef
+
+ class A
+ public static var Cb = Foo
+ endclass
+
+ assert_equal(200, A.Cb(20))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref class variable pointing to a class method in a class
+ # method.
+ lines =<< trim END
+ mnv9script
+ class A
+ static var Cb: func(number): number
+ static def Foo(n: number): number
+ return n * 10
+ enddef
+ static def Init()
+ Cb = Foo
+ enddef
+ static def Bar()
+ assert_equal(200, Cb(20))
+ enddef
+ endclass
+
+ A.Init()
+ A.Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref class variable pointing to a class method in a def method.
+ lines =<< trim END
+ mnv9script
+ class A
+ static var Cb: func(number): number
+ static def Foo(n: number): number
+ return n * 10
+ enddef
+ static def Init()
+ Cb = Foo
+ enddef
+ endclass
+
+ def Bar()
+ A.Init()
+ assert_equal(200, A.Cb(20))
+ enddef
+ Bar()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using a funcref class variable pointing to a class method at script level.
+ lines =<< trim END
+ mnv9script
+ class A
+ static var Cb: func(number): number
+ static def Foo(n: number): number
+ return n * 10
+ enddef
+ static def Init()
+ Cb = Foo
+ enddef
+ endclass
+
+ A.Init()
+ assert_equal(200, A.Cb(20))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using object methods as popup callback functions
+def Test_objmethod_popup_callback()
+ # Use the popup from the script level
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ var selection: number = -1
+ var filterkeys: list<string> = []
+
+ def PopupFilter(id: number, key: string): bool
+ add(this.filterkeys, key)
+ return popup_filter_yesno(id, key)
+ enddef
+
+ def PopupCb(id: number, result: number)
+ this.selection = result ? 100 : 200
+ enddef
+ endclass
+
+ var a = A.new()
+ feedkeys('', 'xt')
+ var winid = popup_create('Y/N?',
+ {filter: a.PopupFilter, callback: a.PopupCb})
+ feedkeys('y', 'xt')
+ popup_close(winid)
+ assert_equal(100, a.selection)
+ assert_equal(['y'], a.filterkeys)
+ feedkeys('', 'xt')
+ winid = popup_create('Y/N?',
+ {filter: a.PopupFilter, callback: a.PopupCb})
+ feedkeys('n', 'xt')
+ popup_close(winid)
+ assert_equal(200, a.selection)
+ assert_equal(['y', 'n'], a.filterkeys)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use the popup from a def function
+ lines =<< trim END
+ mnv9script
+
+ class A
+ var selection: number = -1
+ var filterkeys: list<string> = []
+
+ def PopupFilter(id: number, key: string): bool
+ add(this.filterkeys, key)
+ return popup_filter_yesno(id, key)
+ enddef
+
+ def PopupCb(id: number, result: number)
+ this.selection = result ? 100 : 200
+ enddef
+ endclass
+
+ def Foo()
+ var a = A.new()
+ feedkeys('', 'xt')
+ var winid = popup_create('Y/N?',
+ {filter: a.PopupFilter, callback: a.PopupCb})
+ feedkeys('y', 'xt')
+ popup_close(winid)
+ assert_equal(100, a.selection)
+ assert_equal(['y'], a.filterkeys)
+ feedkeys('', 'xt')
+ winid = popup_create('Y/N?',
+ {filter: a.PopupFilter, callback: a.PopupCb})
+ feedkeys('n', 'xt')
+ popup_close(winid)
+ assert_equal(200, a.selection)
+ assert_equal(['y', 'n'], a.filterkeys)
+ enddef
+ Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using class methods as popup callback functions
+def Test_classmethod_popup_callback()
+ # Use the popup from the script level
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ static var selection: number = -1
+ static var filterkeys: list<string> = []
+
+ static def PopupFilter(id: number, key: string): bool
+ add(filterkeys, key)
+ return popup_filter_yesno(id, key)
+ enddef
+
+ static def PopupCb(id: number, result: number)
+ selection = result ? 100 : 200
+ enddef
+ endclass
+
+ feedkeys('', 'xt')
+ var winid = popup_create('Y/N?',
+ {filter: A.PopupFilter, callback: A.PopupCb})
+ feedkeys('y', 'xt')
+ popup_close(winid)
+ assert_equal(100, A.selection)
+ assert_equal(['y'], A.filterkeys)
+ feedkeys('', 'xt')
+ winid = popup_create('Y/N?',
+ {filter: A.PopupFilter, callback: A.PopupCb})
+ feedkeys('n', 'xt')
+ popup_close(winid)
+ assert_equal(200, A.selection)
+ assert_equal(['y', 'n'], A.filterkeys)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use the popup from a def function
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var selection: number = -1
+ static var filterkeys: list<string> = []
+
+ static def PopupFilter(id: number, key: string): bool
+ add(filterkeys, key)
+ return popup_filter_yesno(id, key)
+ enddef
+
+ static def PopupCb(id: number, result: number)
+ selection = result ? 100 : 200
+ enddef
+ endclass
+
+ def Foo()
+ feedkeys('', 'xt')
+ var winid = popup_create('Y/N?',
+ {filter: A.PopupFilter, callback: A.PopupCb})
+ feedkeys('y', 'xt')
+ popup_close(winid)
+ assert_equal(100, A.selection)
+ assert_equal(['y'], A.filterkeys)
+ feedkeys('', 'xt')
+ winid = popup_create('Y/N?',
+ {filter: A.PopupFilter, callback: A.PopupCb})
+ feedkeys('n', 'xt')
+ popup_close(winid)
+ assert_equal(200, A.selection)
+ assert_equal(['y', 'n'], A.filterkeys)
+ enddef
+ Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an object method as a timer callback function
+def Test_objmethod_timer_callback()
+ # Use the timer callback from script level
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ var timerTick: number = -1
+ def TimerCb(timerID: number)
+ this.timerTick = 6
+ enddef
+ endclass
+
+ var a = A.new()
+ timer_start(0, a.TimerCb)
+ var maxWait = 5
+ while maxWait > 0 && a.timerTick == -1
+ :sleep 10m
+ maxWait -= 1
+ endwhile
+ assert_equal(6, a.timerTick)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use the timer callback from a def function
+ lines =<< trim END
+ mnv9script
+
+ class A
+ var timerTick: number = -1
+ def TimerCb(timerID: number)
+ this.timerTick = 6
+ enddef
+ endclass
+
+ def Foo()
+ var a = A.new()
+ timer_start(0, a.TimerCb)
+ var maxWait = 5
+ while maxWait > 0 && a.timerTick == -1
+ :sleep 10m
+ maxWait -= 1
+ endwhile
+ assert_equal(6, a.timerTick)
+ enddef
+ Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a class method as a timer callback function
+def Test_classmethod_timer_callback()
+ # Use the timer callback from script level
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ static var timerTick: number = -1
+ static def TimerCb(timerID: number)
+ timerTick = 6
+ enddef
+ endclass
+
+ timer_start(0, A.TimerCb)
+ var maxWait = 5
+ while maxWait > 0 && A.timerTick == -1
+ :sleep 10m
+ maxWait -= 1
+ endwhile
+ assert_equal(6, A.timerTick)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use the timer callback from a def function
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var timerTick: number = -1
+ static def TimerCb(timerID: number)
+ timerTick = 6
+ enddef
+ endclass
+
+ def Foo()
+ timer_start(0, A.TimerCb)
+ var maxWait = 5
+ while maxWait > 0 && A.timerTick == -1
+ :sleep 10m
+ maxWait -= 1
+ endwhile
+ assert_equal(6, A.timerTick)
+ enddef
+ Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a class variable as the first and/or second operand of a binary
+" operator.
+def Test_class_variable_as_operands()
+ var lines =<< trim END
+ mnv9script
+ class Tests
+ static var truthy: bool = true
+ public static var TruthyFn: func
+ static var list: list<any> = []
+ static var four: number = 4
+ static var str: string = 'hello'
+
+ static def Str(): string
+ return str
+ enddef
+
+ static def Four(): number
+ return four
+ enddef
+
+ static def List(): list<any>
+ return list
+ enddef
+
+ static def Truthy(): bool
+ return truthy
+ enddef
+
+ def TestOps()
+ assert_true(Tests.truthy == truthy)
+ assert_true(truthy == Tests.truthy)
+ assert_true(Tests.list isnot [])
+ assert_true([] isnot Tests.list)
+ assert_equal(2, Tests.four >> 1)
+ assert_equal(16, 1 << Tests.four)
+ assert_equal(8, Tests.four + four)
+ assert_equal(8, four + Tests.four)
+ assert_equal('hellohello', Tests.str .. str)
+ assert_equal('hellohello', str .. Tests.str)
+
+ # Using class variable for list indexing
+ var l = range(10)
+ assert_equal(4, l[Tests.four])
+ assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2])
+
+ # Using class variable for Dict key
+ var d = {hello: 'abc'}
+ assert_equal('abc', d[Tests.str])
+ enddef
+ endclass
+
+ def TestOps2()
+ assert_true(Tests.truthy == Tests.Truthy())
+ assert_true(Tests.Truthy() == Tests.truthy)
+ assert_true(Tests.truthy == Tests.TruthyFn())
+ assert_true(Tests.TruthyFn() == Tests.truthy)
+ assert_true(Tests.list is Tests.List())
+ assert_true(Tests.List() is Tests.list)
+ assert_equal(2, Tests.four >> 1)
+ assert_equal(16, 1 << Tests.four)
+ assert_equal(8, Tests.four + Tests.Four())
+ assert_equal(8, Tests.Four() + Tests.four)
+ assert_equal('hellohello', Tests.str .. Tests.Str())
+ assert_equal('hellohello', Tests.Str() .. Tests.str)
+
+ # Using class variable for list indexing
+ var l = range(10)
+ assert_equal(4, l[Tests.four])
+ assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2])
+
+ # Using class variable for Dict key
+ var d = {hello: 'abc'}
+ assert_equal('abc', d[Tests.str])
+ enddef
+
+ Tests.TruthyFn = Tests.Truthy
+ var t = Tests.new()
+ t.TestOps()
+ TestOps2()
+
+ assert_true(Tests.truthy == Tests.Truthy())
+ assert_true(Tests.Truthy() == Tests.truthy)
+ assert_true(Tests.truthy == Tests.TruthyFn())
+ assert_true(Tests.TruthyFn() == Tests.truthy)
+ assert_true(Tests.list is Tests.List())
+ assert_true(Tests.List() is Tests.list)
+ assert_equal(2, Tests.four >> 1)
+ assert_equal(16, 1 << Tests.four)
+ assert_equal(8, Tests.four + Tests.Four())
+ assert_equal(8, Tests.Four() + Tests.four)
+ assert_equal('hellohello', Tests.str .. Tests.Str())
+ assert_equal('hellohello', Tests.Str() .. Tests.str)
+
+ # Using class variable for list indexing
+ var l = range(10)
+ assert_equal(4, l[Tests.four])
+ assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2])
+
+ # Using class variable for Dict key
+ var d = {hello: 'abc'}
+ assert_equal('abc', d[Tests.str])
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for checking the type of the key used to access an object dict member.
+def Test_dict_member_key_type_check()
+ var lines =<< trim END
+ mnv9script
+
+ abstract class State
+ var numbers: dict<string> = {0: 'nil', 1: 'unity'}
+ endclass
+
+ class Test extends State
+ def ObjMethodTests()
+ var cursor: number = 0
+ var z: number = 0
+ [this.numbers[cursor]] = ['zero.1']
+ assert_equal({0: 'zero.1', 1: 'unity'}, this.numbers)
+ [this.numbers[string(cursor)], z] = ['zero.2', 1]
+ assert_equal({0: 'zero.2', 1: 'unity'}, this.numbers)
+ [z, this.numbers[string(cursor)]] = [1, 'zero.3']
+ assert_equal({0: 'zero.3', 1: 'unity'}, this.numbers)
+ [this.numbers[cursor], z] = ['zero.4', 1]
+ assert_equal({0: 'zero.4', 1: 'unity'}, this.numbers)
+ [z, this.numbers[cursor]] = [1, 'zero.5']
+ assert_equal({0: 'zero.5', 1: 'unity'}, this.numbers)
+ enddef
+
+ static def ClassMethodTests(that: State)
+ var cursor: number = 0
+ var z: number = 0
+ [that.numbers[cursor]] = ['zero.1']
+ assert_equal({0: 'zero.1', 1: 'unity'}, that.numbers)
+ [that.numbers[string(cursor)], z] = ['zero.2', 1]
+ assert_equal({0: 'zero.2', 1: 'unity'}, that.numbers)
+ [z, that.numbers[string(cursor)]] = [1, 'zero.3']
+ assert_equal({0: 'zero.3', 1: 'unity'}, that.numbers)
+ [that.numbers[cursor], z] = ['zero.4', 1]
+ assert_equal({0: 'zero.4', 1: 'unity'}, that.numbers)
+ [z, that.numbers[cursor]] = [1, 'zero.5']
+ assert_equal({0: 'zero.5', 1: 'unity'}, that.numbers)
+ enddef
+
+ def new()
+ enddef
+
+ def newMethodTests()
+ var cursor: number = 0
+ var z: number
+ [this.numbers[cursor]] = ['zero.1']
+ assert_equal({0: 'zero.1', 1: 'unity'}, this.numbers)
+ [this.numbers[string(cursor)], z] = ['zero.2', 1]
+ assert_equal({0: 'zero.2', 1: 'unity'}, this.numbers)
+ [z, this.numbers[string(cursor)]] = [1, 'zero.3']
+ assert_equal({0: 'zero.3', 1: 'unity'}, this.numbers)
+ [this.numbers[cursor], z] = ['zero.4', 1]
+ assert_equal({0: 'zero.4', 1: 'unity'}, this.numbers)
+ [z, this.numbers[cursor]] = [1, 'zero.5']
+ assert_equal({0: 'zero.5', 1: 'unity'}, this.numbers)
+ enddef
+ endclass
+
+ def DefFuncTests(that: Test)
+ var cursor: number = 0
+ var z: number
+ [that.numbers[cursor]] = ['zero.1']
+ assert_equal({0: 'zero.1', 1: 'unity'}, that.numbers)
+ [that.numbers[string(cursor)], z] = ['zero.2', 1]
+ assert_equal({0: 'zero.2', 1: 'unity'}, that.numbers)
+ [z, that.numbers[string(cursor)]] = [1, 'zero.3']
+ assert_equal({0: 'zero.3', 1: 'unity'}, that.numbers)
+ [that.numbers[cursor], z] = ['zero.4', 1]
+ assert_equal({0: 'zero.4', 1: 'unity'}, that.numbers)
+ [z, that.numbers[cursor]] = [1, 'zero.5']
+ assert_equal({0: 'zero.5', 1: 'unity'}, that.numbers)
+ enddef
+
+ Test.newMethodTests()
+ Test.new().ObjMethodTests()
+ Test.ClassMethodTests(Test.new())
+ DefFuncTests(Test.new())
+
+ const test: Test = Test.new()
+ var cursor: number = 0
+ [test.numbers[cursor], cursor] = ['zero', 1]
+ [cursor, test.numbers[cursor]] = [1, 'one']
+ assert_equal({0: 'zero', 1: 'one'}, test.numbers)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ var numbers: dict<string> = {a: '1', b: '2'}
+
+ def new()
+ enddef
+
+ def Foo()
+ var z: number
+ [this.numbers.a, z] = [{}, 10]
+ enddef
+ endclass
+
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected string but got dict<any>', 2)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ var numbers: dict<number> = {a: 1, b: 2}
+
+ def new()
+ enddef
+
+ def Foo()
+ var x: string = 'a'
+ var y: number
+ [this.numbers[x], y] = [{}, 10]
+ enddef
+ endclass
+
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got dict<any>', 3)
+enddef
+
+def Test_compile_many_def_functions_in_funcref_instr()
+ # This used to crash MNV. This is reproducible only when run on new instance
+ # of MNV.
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ def new()
+ this.TakeFunc(this.F00)
+ enddef
+
+ def TakeFunc(F: func)
+ enddef
+
+ def F00()
+ this.F01()
+ this.F02()
+ this.F03()
+ this.F04()
+ this.F05()
+ this.F06()
+ this.F07()
+ this.F08()
+ this.F09()
+ this.F10()
+ this.F11()
+ this.F12()
+ this.F13()
+ this.F14()
+ this.F15()
+ this.F16()
+ this.F17()
+ this.F18()
+ this.F19()
+ this.F20()
+ this.F21()
+ this.F22()
+ this.F23()
+ this.F24()
+ this.F25()
+ this.F26()
+ this.F27()
+ this.F28()
+ this.F29()
+ this.F30()
+ this.F31()
+ this.F32()
+ this.F33()
+ this.F34()
+ this.F35()
+ this.F36()
+ this.F37()
+ this.F38()
+ this.F39()
+ this.F40()
+ this.F41()
+ this.F42()
+ this.F43()
+ this.F44()
+ this.F45()
+ this.F46()
+ this.F47()
+ enddef
+
+ def F01()
+ enddef
+ def F02()
+ enddef
+ def F03()
+ enddef
+ def F04()
+ enddef
+ def F05()
+ enddef
+ def F06()
+ enddef
+ def F07()
+ enddef
+ def F08()
+ enddef
+ def F09()
+ enddef
+ def F10()
+ enddef
+ def F11()
+ enddef
+ def F12()
+ enddef
+ def F13()
+ enddef
+ def F14()
+ enddef
+ def F15()
+ enddef
+ def F16()
+ enddef
+ def F17()
+ enddef
+ def F18()
+ enddef
+ def F19()
+ enddef
+ def F20()
+ enddef
+ def F21()
+ enddef
+ def F22()
+ enddef
+ def F23()
+ enddef
+ def F24()
+ enddef
+ def F25()
+ enddef
+ def F26()
+ enddef
+ def F27()
+ enddef
+ def F28()
+ enddef
+ def F29()
+ enddef
+ def F30()
+ enddef
+ def F31()
+ enddef
+ def F32()
+ enddef
+ def F33()
+ enddef
+ def F34()
+ enddef
+ def F35()
+ enddef
+ def F36()
+ enddef
+ def F37()
+ enddef
+ def F38()
+ enddef
+ def F39()
+ enddef
+ def F40()
+ enddef
+ def F41()
+ enddef
+ def F42()
+ enddef
+ def F43()
+ enddef
+ def F44()
+ enddef
+ def F45()
+ enddef
+ def F46()
+ enddef
+ def F47()
+ enddef
+ endclass
+
+ A.new()
+ END
+ writefile(lines, 'Xscript', 'D')
+ g:RunMNV([], [], '-u NONE -S Xscript -c qa')
+ assert_equal(0, v:shell_error)
+enddef
+
+" Test for 'final' class and object variables
+def Test_final_class_object_variable()
+ # Test for changing a final object variable from an object function
+ var lines =<< trim END
+ mnv9script
+ class A
+ final foo: string = "abc"
+ def Foo()
+ this.foo = "def"
+ enddef
+ endclass
+ defcompile A.Foo
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "foo" in class "A"', 1)
+
+ # Test for changing a final object variable from the 'new' function
+ lines =<< trim END
+ mnv9script
+ class A
+ final s1: string
+ final s2: string
+ def new(this.s1)
+ this.s2 = 'def'
+ enddef
+ endclass
+ var a = A.new('abc')
+ assert_equal('abc', a.s1)
+ assert_equal('def', a.s2)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for a final class variable
+ lines =<< trim END
+ mnv9script
+ class A
+ static final s1: string = "abc"
+ endclass
+ assert_equal('abc', A.s1)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for changing a final class variable from a class function
+ lines =<< trim END
+ mnv9script
+ class A
+ static final s1: string = "abc"
+ static def Foo()
+ s1 = "def"
+ enddef
+ endclass
+ A.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1)
+
+ # Test for changing a public final class variable at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public static final s1: string = "abc"
+ endclass
+ assert_equal('abc', A.s1)
+ A.s1 = 'def'
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 6)
+
+ # Test for changing a public final class variable from a class function
+ lines =<< trim END
+ mnv9script
+ class A
+ public static final s1: string = "abc"
+ static def Foo()
+ s1 = "def"
+ enddef
+ endclass
+ A.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1)
+
+ # Test for changing a public final class variable from a function
+ lines =<< trim END
+ mnv9script
+ class A
+ public static final s1: string = "abc"
+ endclass
+ def Foo()
+ A.s1 = 'def'
+ enddef
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1)
+
+ # Test for using a final variable of composite type
+ lines =<< trim END
+ mnv9script
+ class A
+ public final l: list<number>
+ def new()
+ this.l = [1, 2]
+ enddef
+ def Foo()
+ this.l[0] = 3
+ this.l->add(4)
+ enddef
+ endclass
+ var a = A.new()
+ assert_equal([1, 2], a.l)
+ a.Foo()
+ assert_equal([3, 2, 4], a.l)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for changing a final variable of composite type from another object
+ # function
+ lines =<< trim END
+ mnv9script
+ class A
+ public final l: list<number> = [1, 2]
+ def Foo()
+ this.l = [3, 4]
+ enddef
+ endclass
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 1)
+
+ # Test for modifying a final variable of composite type at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public final l: list<number> = [1, 2]
+ endclass
+ var a = A.new()
+ a.l[0] = 3
+ a.l->add(4)
+ assert_equal([3, 2, 4], a.l)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for modifying a final variable of composite type from a function
+ lines =<< trim END
+ mnv9script
+ class A
+ public final l: list<number> = [1, 2]
+ endclass
+ def Foo()
+ var a = A.new()
+ a.l[0] = 3
+ a.l->add(4)
+ assert_equal([3, 2, 4], a.l)
+ enddef
+ Foo()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for modifying a final variable of composite type from another object
+ # function
+ lines =<< trim END
+ mnv9script
+ class A
+ public final l: list<number> = [1, 2]
+ def Foo()
+ this.l[0] = 3
+ this.l->add(4)
+ enddef
+ endclass
+ var a = A.new()
+ a.Foo()
+ assert_equal([3, 2, 4], a.l)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for assigning a new value to a final variable of composite type at
+ # script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public final l: list<number> = [1, 2]
+ endclass
+ var a = A.new()
+ a.l = [3, 4]
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 6)
+
+ # Test for assigning a new value to a final variable of composite type from
+ # another object function
+ lines =<< trim END
+ mnv9script
+ class A
+ public final l: list<number> = [1, 2]
+ def Foo()
+ this.l = [3, 4]
+ enddef
+ endclass
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 1)
+
+ # Test for assigning a new value to a final variable of composite type from
+ # another function
+ lines =<< trim END
+ mnv9script
+ class A
+ public final l: list<number> = [1, 2]
+ endclass
+ def Foo()
+ var a = A.new()
+ a.l = [3, 4]
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 2)
+
+ # Error case: Use 'final' with just a variable name
+ lines =<< trim END
+ mnv9script
+ class A
+ final foo
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # Error case: Use 'final' followed by 'public'
+ lines =<< trim END
+ mnv9script
+ class A
+ final public foo: number
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # Error case: Use 'final' followed by 'static'
+ lines =<< trim END
+ mnv9script
+ class A
+ final static foo: number
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # Error case: 'final' cannot be used in an interface
+ lines =<< trim END
+ mnv9script
+ interface A
+ final foo: number = 10
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1408: Final variable not supported in an interface', 3)
+
+ # Error case: 'final' not supported for an object method
+ lines =<< trim END
+ mnv9script
+ class A
+ final def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # Error case: 'final' not supported for a class method
+ lines =<< trim END
+ mnv9script
+ class A
+ static final def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+enddef
+
+" Test for 'const' class and object variables
+def Test_const_class_object_variable()
+ # Test for changing a const object variable from an object function
+ var lines =<< trim END
+ mnv9script
+ class A
+ const foo: string = "abc"
+ def Foo()
+ this.foo = "def"
+ enddef
+ endclass
+ defcompile A.Foo
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "foo" in class "A"', 1)
+
+ # Test for changing a const object variable from the 'new' function
+ lines =<< trim END
+ mnv9script
+ class A
+ const s1: string
+ const s2: string
+ def new(this.s1)
+ this.s2 = 'def'
+ enddef
+ endclass
+ var a = A.new('abc')
+ assert_equal('abc', a.s1)
+ assert_equal('def', a.s2)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for changing a const object variable from an object method called from
+ # the 'new' function
+ lines =<< trim END
+ mnv9script
+ class A
+ const s1: string = 'abc'
+ def new()
+ this.ChangeStr()
+ enddef
+ def ChangeStr()
+ this.s1 = 'def'
+ enddef
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1)
+
+ # Test for a const class variable
+ lines =<< trim END
+ mnv9script
+ class A
+ static const s1: string = "abc"
+ endclass
+ assert_equal('abc', A.s1)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for changing a const class variable from a class function
+ lines =<< trim END
+ mnv9script
+ class A
+ static const s1: string = "abc"
+ static def Foo()
+ s1 = "def"
+ enddef
+ endclass
+ A.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1)
+
+ # Test for changing a public const class variable at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public static const s1: string = "abc"
+ endclass
+ assert_equal('abc', A.s1)
+ A.s1 = 'def'
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 6)
+
+ # Test for changing a public const class variable from a class function
+ lines =<< trim END
+ mnv9script
+ class A
+ public static const s1: string = "abc"
+ static def Foo()
+ s1 = "def"
+ enddef
+ endclass
+ A.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1)
+
+ # Test for changing a public const class variable from a function
+ lines =<< trim END
+ mnv9script
+ class A
+ public static const s1: string = "abc"
+ endclass
+ def Foo()
+ A.s1 = 'def'
+ enddef
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1)
+
+ # Test for changing a const List item from an object function
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number>
+ def new()
+ this.l = [1, 2]
+ enddef
+ def Foo()
+ this.l[0] = 3
+ enddef
+ endclass
+ var a = A.new()
+ assert_equal([1, 2], a.l)
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1119: Cannot change locked list item', 1)
+
+ # Test for adding a value to a const List from an object function
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number>
+ def new()
+ this.l = [1, 2]
+ enddef
+ def Foo()
+ this.l->add(3)
+ enddef
+ endclass
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: add() argument', 1)
+
+ # Test for reassigning a const List from an object function
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ def Foo()
+ this.l = [3, 4]
+ enddef
+ endclass
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 1)
+
+ # Test for changing a const List item at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ endclass
+ var a = A.new()
+ a.l[0] = 3
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked:', 6)
+
+ # Test for adding a value to a const List item at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ endclass
+ var a = A.new()
+ a.l->add(4)
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked:', 6)
+
+ # Test for changing a const List item from a function
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ endclass
+ def Foo()
+ var a = A.new()
+ a.l[0] = 3
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1119: Cannot change locked list item', 2)
+
+ # Test for adding a value to a const List item from a function
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ endclass
+ def Foo()
+ var a = A.new()
+ a.l->add(4)
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: add() argument', 2)
+
+ # Test for changing a const List item from an object method
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ def Foo()
+ this.l[0] = 3
+ enddef
+ endclass
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1119: Cannot change locked list item', 1)
+
+ # Test for adding a value to a const List item from an object method
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ def Foo()
+ this.l->add(4)
+ enddef
+ endclass
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: add() argument', 1)
+
+ # Test for reassigning a const List object variable at script level
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ endclass
+ var a = A.new()
+ a.l = [3, 4]
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 6)
+
+ # Test for reassigning a const List object variable from an object method
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ def Foo()
+ this.l = [3, 4]
+ enddef
+ endclass
+ var a = A.new()
+ a.Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 1)
+
+ # Test for reassigning a const List object variable from another function
+ lines =<< trim END
+ mnv9script
+ class A
+ public const l: list<number> = [1, 2]
+ endclass
+ def Foo()
+ var a = A.new()
+ a.l = [3, 4]
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 2)
+
+ # Error case: Use 'const' with just a variable name
+ lines =<< trim END
+ mnv9script
+ class A
+ const foo
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # Error case: Use 'const' followed by 'public'
+ lines =<< trim END
+ mnv9script
+ class A
+ const public foo: number
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # Error case: Use 'const' followed by 'static'
+ lines =<< trim END
+ mnv9script
+ class A
+ const static foo: number
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # Error case: 'const' cannot be used in an interface
+ lines =<< trim END
+ mnv9script
+ interface A
+ const foo: number = 10
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1410: Const variable not supported in an interface', 3)
+
+ # Error case: 'const' not supported for an object method
+ lines =<< trim END
+ mnv9script
+ class A
+ const def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+
+ # Error case: 'const' not supported for a class method
+ lines =<< trim END
+ mnv9script
+ class A
+ static const def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
+enddef
+
+" Test for compiling class/object methods using :defcompile
+def Test_defcompile_class()
+ # defcompile all the classes in the current script
+ var lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ var i = 10
+ enddef
+ endclass
+ class B
+ def Bar()
+ var i = 20
+ xxx
+ enddef
+ endclass
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E476: Invalid command: xxx', 2)
+
+ # defcompile a specific class
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ xxx
+ enddef
+ endclass
+ class B
+ def Bar()
+ yyy
+ enddef
+ endclass
+ defcompile B
+ END
+ v9.CheckSourceFailure(lines, 'E476: Invalid command: yyy', 1)
+
+ # defcompile a non-class
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo()
+ enddef
+ endclass
+ var X: list<number> = []
+ defcompile X
+ END
+ v9.CheckSourceFailure(lines, 'E1061: Cannot find function X', 7)
+
+ # defcompile a class twice
+ lines =<< trim END
+ mnv9script
+ class A
+ def new()
+ enddef
+ endclass
+ defcompile A
+ defcompile A
+ assert_equal('Function A.new does not need compiling', v:statusmsg)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # defcompile should not compile an imported class
+ lines =<< trim END
+ mnv9script
+ export class A
+ def Foo()
+ xxx
+ enddef
+ endclass
+ END
+ writefile(lines, 'Xdefcompileimport.mnv', 'D')
+ lines =<< trim END
+ mnv9script
+
+ import './Xdefcompileimport.mnv'
+ class B
+ endclass
+ defcompile
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for cases common to all the object builtin methods
+def Test_object_builtin_method()
+ var lines =<< trim END
+ mnv9script
+ class A
+ def abc()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: abc()', 3)
+
+ for funcname in ["len", "string", "empty"]
+ lines =<< trim eval END
+ mnv9script
+ class A
+ static def {funcname}(): number
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1413: Builtin class method not supported', 3)
+ endfor
+enddef
+
+" Test for using the empty() builtin method with an object
+" This is a legacy function to use the test_garbagecollect_now() function.
+func Test_object_empty()
+ let lines =<< trim END
+ mnv9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ assert_equal(true, empty(afoo))
+ assert_equal(true, afoo->empty())
+ enddef
+
+ var a = A.new()
+ assert_equal(1, empty(a))
+ assert_equal(1, a->empty())
+ test_garbagecollect_now()
+ assert_equal(1, empty(a))
+ Foo()
+ test_garbagecollect_now()
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " empty() should return 1 without a builtin method
+ let lines =<< trim END
+ mnv9script
+ class A
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ assert_equal(1, empty(afoo))
+ enddef
+
+ var a = A.new()
+ assert_equal(1, empty(a))
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Unsupported signature for the empty() method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def empty()
+ enddef
+ endclass
+ END
+ call v9.CheckSourceFailure(lines, 'E1383: Method "empty": type mismatch, expected func(): bool but got func()', 4)
+
+ " Error when calling the empty() method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def empty(): bool
+ throw "Failed to check emptiness"
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ var i = empty(afoo)
+ enddef
+
+ var a = A.new()
+ assert_fails('empty(a)', 'Failed to check emptiness')
+ assert_fails('Foo()', 'Failed to check emptiness')
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call empty() using an object from a script
+ let lines =<< trim END
+ mnv9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+ var afoo = A.new()
+ assert_equal(true, afoo.empty())
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call empty() using an object from a method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+ def Foo()
+ var afoo = A.new()
+ assert_equal(true, afoo.empty())
+ enddef
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call empty() using "this" from an object method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ def Foo(): bool
+ return this.empty()
+ enddef
+ endclass
+ def Bar()
+ var abar = A.new()
+ assert_equal(true, abar.Foo())
+ enddef
+ Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Call empty() from a derived object
+ let lines =<< trim END
+ mnv9script
+ class A
+ def empty(): bool
+ return false
+ enddef
+ endclass
+ class B extends A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+ def Foo(afoo: A)
+ assert_equal(true, empty(afoo))
+ var bfoo = B.new()
+ assert_equal(true, empty(bfoo))
+ enddef
+ var b = B.new()
+ assert_equal(1, empty(b))
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Invoking empty method using an interface
+ let lines =<< trim END
+ mnv9script
+ interface A
+ def empty(): bool
+ endinterface
+ class B implements A
+ def empty(): bool
+ return false
+ enddef
+ endclass
+ def Foo(a: A)
+ assert_equal(false, empty(a))
+ enddef
+ var b = B.new()
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for using the len() builtin method with an object
+" This is a legacy function to use the test_garbagecollect_now() function.
+func Test_object_length()
+ let lines =<< trim END
+ mnv9script
+ class A
+ var mylen: number = 0
+ def new(n: number)
+ this.mylen = n
+ enddef
+ def len(): number
+ return this.mylen
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new(12)
+ assert_equal(12, len(afoo))
+ assert_equal(12, afoo->len())
+ enddef
+
+ var a = A.new(22)
+ assert_equal(22, len(a))
+ assert_equal(22, a->len())
+ test_garbagecollect_now()
+ assert_equal(22, len(a))
+ Foo()
+ test_garbagecollect_now()
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " len() should return 0 without a builtin method
+ let lines =<< trim END
+ mnv9script
+ class A
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ assert_equal(0, len(afoo))
+ enddef
+
+ var a = A.new()
+ assert_equal(0, len(a))
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Unsupported signature for the len() method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def len()
+ enddef
+ endclass
+ END
+ call v9.CheckSourceFailure(lines, 'E1383: Method "len": type mismatch, expected func(): number but got func()', 4)
+
+ " Error when calling the len() method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def len(): number
+ throw "Failed to compute length"
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ var i = len(afoo)
+ enddef
+
+ var a = A.new()
+ assert_fails('len(a)', 'Failed to compute length')
+ assert_fails('Foo()', 'Failed to compute length')
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call len() using an object from a script
+ let lines =<< trim END
+ mnv9script
+ class A
+ def len(): number
+ return 5
+ enddef
+ endclass
+ var afoo = A.new()
+ assert_equal(5, afoo.len())
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call len() using an object from a method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def len(): number
+ return 5
+ enddef
+ endclass
+ def Foo()
+ var afoo = A.new()
+ assert_equal(5, afoo.len())
+ enddef
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call len() using "this" from an object method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def len(): number
+ return 8
+ enddef
+ def Foo(): number
+ return this.len()
+ enddef
+ endclass
+ def Bar()
+ var abar = A.new()
+ assert_equal(8, abar.Foo())
+ enddef
+ Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Call len() from a derived object
+ let lines =<< trim END
+ mnv9script
+ class A
+ def len(): number
+ return 10
+ enddef
+ endclass
+ class B extends A
+ def len(): number
+ return 20
+ enddef
+ endclass
+ def Foo(afoo: A)
+ assert_equal(20, len(afoo))
+ var bfoo = B.new()
+ assert_equal(20, len(bfoo))
+ enddef
+ var b = B.new()
+ assert_equal(20, len(b))
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Invoking len method using an interface
+ let lines =<< trim END
+ mnv9script
+ interface A
+ def len(): number
+ endinterface
+ class B implements A
+ def len(): number
+ return 123
+ enddef
+ endclass
+ def Foo(a: A)
+ assert_equal(123, len(a))
+ enddef
+ var b = B.new()
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for using the string() builtin method with an object
+" This is a legacy function to use the test_garbagecollect_now() function.
+func Test_object_string()
+ let lines =<< trim END
+ mnv9script
+ class A
+ var name: string
+ def string(): string
+ return this.name
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new("foo-A")
+ assert_equal('foo-A', string(afoo))
+ assert_equal('foo-A', afoo->string())
+ enddef
+
+ var a = A.new("script-A")
+ assert_equal('script-A', string(a))
+ assert_equal('script-A', a->string())
+ assert_equal(['script-A'], execute('echo a')->split("\n"))
+ test_garbagecollect_now()
+ assert_equal('script-A', string(a))
+ Foo()
+ test_garbagecollect_now()
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " string() should return "object of A {}" without a builtin method
+ let lines =<< trim END
+ mnv9script
+ class A
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ assert_equal('object of A {}', string(afoo))
+ enddef
+
+ var a = A.new()
+ assert_equal('object of A {}', string(a))
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Unsupported signature for the string() method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def string()
+ enddef
+ endclass
+ END
+ call v9.CheckSourceFailure(lines, 'E1383: Method "string": type mismatch, expected func(): string but got func()', 4)
+
+ " Error when calling the string() method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def string(): string
+ throw "Failed to get text"
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ var i = string(afoo)
+ enddef
+
+ var a = A.new()
+ assert_fails('string(a)', 'Failed to get text')
+ assert_fails('Foo()', 'Failed to get text')
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call string() using an object from a script
+ let lines =<< trim END
+ mnv9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ endclass
+ var afoo = A.new()
+ assert_equal('A', afoo.string())
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call string() using an object from a method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ endclass
+ def Foo()
+ var afoo = A.new()
+ assert_equal('A', afoo.string())
+ enddef
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call string() using "this" from an object method
+ let lines =<< trim END
+ mnv9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ def Foo(): string
+ return this.string()
+ enddef
+ endclass
+ def Bar()
+ var abar = A.new()
+ assert_equal('A', abar.string())
+ enddef
+ Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Call string() from a derived object
+ let lines =<< trim END
+ mnv9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ endclass
+ class B extends A
+ def string(): string
+ return 'B'
+ enddef
+ endclass
+ def Foo(afoo: A)
+ assert_equal('B', string(afoo))
+ var bfoo = B.new()
+ assert_equal('B', string(bfoo))
+ enddef
+ var b = B.new()
+ assert_equal('B', string(b))
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Invoking string method using an interface
+ let lines =<< trim END
+ mnv9script
+ interface A
+ def string(): string
+ endinterface
+ class B implements A
+ def string(): string
+ return 'B'
+ enddef
+ endclass
+ def Foo(a: A)
+ assert_equal('B', string(a))
+ enddef
+ var b = B.new()
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for using the string() builtin method with an object's method
+def Test_method_string()
+ var lines =<< trim END
+ mnv9script
+ class A
+ def F()
+ enddef
+ endclass
+ assert_match('function(''<SNR>\d\+_A\.F'')', string(A.new().F))
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using a class in the class definition
+def Test_Ref_Class_Within_Same_Class()
+ var lines =<< trim END
+ mnv9script
+ class A
+ var n: number = 0
+ def Equals(other: A): bool
+ return this.n == other.n
+ enddef
+ endclass
+
+ var a1 = A.new(10)
+ var a2 = A.new(10)
+ var a3 = A.new(20)
+ assert_equal(true, a1.Equals(a2))
+ assert_equal(false, a2.Equals(a3))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var num: number
+ def Clone(): Foo
+ return Foo.new(this.num)
+ enddef
+ endclass
+
+ var f1 = Foo.new(1)
+
+ def F()
+ var f2: Foo = f1.Clone()
+ assert_equal(false, f2 is f1)
+ assert_equal(true, f2.num == f1.num)
+ enddef
+ F()
+
+ var f3: Foo = f1.Clone()
+ assert_equal(false, f3 is f1)
+ assert_equal(true, f3.num == f1.num)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Test for trying to use a class to extend when defining the same class
+ lines =<< trim END
+ mnv9script
+ class A extends A
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1354: Cannot extend A', 3)
+
+ # Test for trying to use a class to implement when defining the same class
+ lines =<< trim END
+ mnv9script
+ class A implements A
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1347: Not a valid interface: A', 3)
+enddef
+
+" Test for comparing a class referencing itself
+def Test_Object_Compare_With_Recursive_Class_Ref()
+ var lines =<< trim END
+ mnv9script
+
+ class C
+ public var nest: C
+ endclass
+
+ var o1 = C.new()
+ o1.nest = o1
+
+ var result = o1 == o1
+ assert_equal(true, result)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class C
+ public var nest: C
+ endclass
+ var o1 = C.new()
+ var o2 = C.new(C.new())
+
+ var result = o1 == o2
+ assert_equal(false, result)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class C
+ var nest1: C
+ var nest2: C
+ def Init(n1: C, n2: C)
+ this.nest1 = n1
+ this.nest2 = n2
+ enddef
+ endclass
+
+ var o1 = C.new()
+ var o2 = C.new()
+ o1.Init(o1, o2)
+ o2.Init(o2, o1)
+
+ var result = o1 == o2
+ assert_equal(true, result)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for comparing a class with nesting objects
+def Test_Object_Compare_With_Nesting_Objects()
+ # On a compare, after mnv equal recurses 1000 times, not finding an unequal,
+ # return the compare is equal.
+ # Test that limit
+
+ var lines =<< trim END
+ mnv9script
+ class C
+ public var n: number
+ public var nest: C
+
+ # Create a "C" that chains/nests to indicated depth.
+ # return {head: firstC, tail: lastC}
+ static def CreateNested(depth: number): dict<C>
+ var first = C.new(1, null_object)
+ var last = first
+ for i in range(2, depth)
+ last.nest = C.new(i, null_object)
+ last = last.nest
+ endfor
+ return {head: first, tail: last}
+ enddef
+
+ # Return pointer to nth item in chain.
+ def GetLink(depth: number): C
+ var count = 1
+ var p: C = this
+ while count < depth
+ p = p.nest
+ if p == null
+ throw "too deep"
+ endif
+ count += 1
+ endwhile
+ return p
+ enddef
+
+ # Return the length of the chain
+ def len(): number
+ var count = 1
+ var p: C = this
+ while p.nest != null
+ p = p.nest
+ count += 1
+ endwhile
+ return count
+ enddef
+ endclass
+
+ var chain = C.CreateNested(3)
+ var s = "object of C {n: 1, nest: object of C {n: 2, nest: object of C {n: 3, nest: object of [unknown]}}}"
+ assert_equal(s, string(chain.head))
+ assert_equal(3, chain.head->len())
+
+ var chain1 = C.CreateNested(100)
+ var chain2 = C.CreateNested(100)
+ assert_true(chain1.head == chain2.head)
+
+ # modify the tail of chain2, compare not equal
+ chain2.tail.n = 123456
+ assert_true(chain1.head != chain2.head)
+
+ # a tail of a different length compares not equal
+ chain2 = C.CreateNested(101)
+ assert_true(chain1.head != chain2.head)
+
+ chain1 = C.CreateNested(1000)
+ chain2 = C.CreateNested(1000)
+ assert_true(chain1.head == chain2.head)
+
+ # modify the tail of chain2, compare not equal
+ chain2.tail.n = 123456
+ assert_true(chain1.head != chain2.head)
+
+ # try a chain longer that the limit
+ chain1 = C.CreateNested(1001)
+ chain2 = C.CreateNested(1001)
+ assert_true(chain1.head == chain2.head)
+
+ # modify the tail, but still equal
+ chain2.tail.n = 123456
+ assert_true(chain1.head == chain2.head)
+
+ # remove 2 items from front, shorten the chain by two.
+ chain1.head = chain1.head.GetLink(3)
+ chain2.head = chain2.head.GetLink(3)
+ assert_equal(3, chain1.head.n)
+ assert_equal(3, chain2.head.n)
+ assert_equal(999, chain1.head->len())
+ assert_equal(999, chain2.head->len())
+ # Now less than the limit, compare not equal
+ assert_true(chain1.head != chain2.head)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using a compound operator from a lambda function in an object method
+def Test_compound_op_in_objmethod_lambda()
+ # Test using the "+=" operator
+ var lines =<< trim END
+ mnv9script
+ class A
+ var n: number = 10
+ def Foo()
+ var Fn = () => {
+ this.n += 1
+ }
+ Fn()
+ enddef
+ endclass
+
+ var a = A.new()
+ a.Foo()
+ assert_equal(11, a.n)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Test using the "..=" operator
+ lines =<< trim END
+ mnv9script
+ class A
+ var s: string = "a"
+ def Foo()
+ var Fn = () => {
+ this.s ..= "a"
+ }
+ Fn()
+ enddef
+ endclass
+
+ var a = A.new()
+ a.Foo()
+ a.Foo()
+ assert_equal("aaa", a.s)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using test_refcount() with a class and an object
+def Test_class_object_refcount()
+ var lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ var a: A = A.new()
+ assert_equal(2, test_refcount(A))
+ assert_equal(1, test_refcount(a))
+ var b = a
+ assert_equal(2, test_refcount(A))
+ assert_equal(2, test_refcount(a))
+ assert_equal(2, test_refcount(b))
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" call a lambda function in one object from another object
+def Test_lambda_invocation_across_classes()
+ var lines =<< trim END
+ mnv9script
+ class A
+ var s: string = "foo"
+ def GetFn(): func
+ var Fn = (): string => {
+ return this.s
+ }
+ return Fn
+ enddef
+ endclass
+
+ class B
+ var s: string = "bar"
+ def GetFn(): func
+ var a = A.new()
+ return a.GetFn()
+ enddef
+ endclass
+
+ var b = B.new()
+ var Fn = b.GetFn()
+ assert_equal("foo", Fn())
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using a class member which is an object of the current class
+def Test_current_class_object_class_member()
+ var lines =<< trim END
+ mnv9script
+ class A
+ public static var obj1: A = A.new(10)
+ var n: number
+ endclass
+ defcompile
+ assert_equal(10, A.obj1.n)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for updating a base class variable from a base class method without the
+" class name. This used to crash MNV (Github issue #14352).
+def Test_use_base_class_variable_from_base_class_method()
+ var lines =<< trim END
+ mnv9script
+
+ class DictKeyClass
+ static var _obj_id_count = 1
+ def _GenerateKey()
+ _obj_id_count += 1
+ enddef
+ static def GetIdCount(): number
+ return _obj_id_count
+ enddef
+ endclass
+
+ class C extends DictKeyClass
+ def F()
+ this._GenerateKey()
+ enddef
+ endclass
+
+ C.new().F()
+ assert_equal(2, DictKeyClass.GetIdCount())
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for accessing protected funcref object and class variables
+def Test_protected_funcref()
+ # protected funcref object variable
+ var lines =<< trim END
+ mnv9script
+ class Test1
+ const _Id: func(any): any = (v) => v
+ endclass
+ var n = Test1.new()._Id(1)
+ END
+ v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_Id" in class "Test1"', 5)
+
+ # protected funcref class variable
+ lines =<< trim END
+ mnv9script
+ class Test2
+ static const _Id: func(any): any = (v) => v
+ endclass
+ var n = Test2._Id(2)
+ END
+ v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_Id" in class "Test2"', 5)
+enddef
+
+" Test for using lambda block in classes
+def Test_lambda_block_in_class()
+ # This used to crash MNV
+ var lines =<< trim END
+ mnv9script
+ class IdClass1
+ const Id: func(number): number = (num: number): number => {
+ # Return a ID
+ return num * 10
+ }
+ endclass
+ var id = IdClass1.new()
+ assert_equal(20, id.Id(2))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # This used to crash MNV
+ lines =<< trim END
+ mnv9script
+ class IdClass2
+ static const Id: func(number): number = (num: number): number => {
+ # Return a ID
+ return num * 2
+ }
+ endclass
+ assert_equal(16, IdClass2.Id(8))
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for defcompiling an abstract method
+def Test_abstract_method_defcompile()
+ # Compile an abstract class with abstract object methods
+ var lines =<< trim END
+ mnv9script
+ abstract class A
+ abstract def Foo(): string
+ abstract def Bar(): list<string>
+ endclass
+ defcompile
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Compile a concrete object method in an abstract class
+ lines =<< trim END
+ mnv9script
+ abstract class A
+ abstract def Foo(): string
+ abstract def Bar(): list<string>
+ def Baz(): string
+ pass
+ enddef
+ endclass
+ defcompile
+ END
+ v9.CheckScriptFailure(lines, 'E476: Invalid command: pass', 1)
+
+ # Compile a concrete class method in an abstract class
+ lines =<< trim END
+ mnv9script
+ abstract class A
+ abstract def Foo(): string
+ abstract def Bar(): list<string>
+ static def Baz(): string
+ pass
+ enddef
+ endclass
+ defcompile
+ END
+ v9.CheckScriptFailure(lines, 'E476: Invalid command: pass', 1)
+enddef
+
+" Test for defining a class in a function
+def Test_class_definition_in_a_function()
+ var lines =<< trim END
+ mnv9script
+ def Foo()
+ class A
+ endclass
+ enddef
+ defcompile
+ END
+ v9.CheckScriptFailure(lines, 'E1429: Class can only be used in a script', 1)
+enddef
+
+" Test for using [] with a class and an object
+def Test_class_object_index()
+ var lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ A[10] = 1
+ END
+ v9.CheckScriptFailure(lines, 'E689: Index not allowed after a class: A[10] = 1', 4)
+
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ var a = A.new()
+ a[10] = 1
+ END
+ v9.CheckScriptFailure(lines, 'E689: Index not allowed after a object: a[10] = 1', 5)
+enddef
+
+def Test_class_member_init_typecheck()
+ # Ensure the class member is assigned its declared type.
+ var lines =<< trim END
+ mnv9script
+ class S
+ static var l: list<string> = []
+ endclass
+ S.l->add(123)
+ END
+ v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number', 5)
+
+ # Ensure the initializer value and the declared type match.
+ lines =<< trim END
+ mnv9script
+ class S
+ var l: list<string> = [1, 2, 3]
+ endclass
+ var o = S.new()
+ END
+ v9.CheckScriptFailure(lines, 'E1382: Variable "l": type mismatch, expected list<string> but got list<number>')
+
+ # Ensure the class member is assigned its declared type.
+ lines =<< trim END
+ mnv9script
+ class S
+ var l: list<string> = []
+ endclass
+ var o = S.new()
+ o.l->add(123)
+ END
+ v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number', 6)
+enddef
+
+def Test_class_cast()
+ var lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B extends A
+ var mylen: number
+ endclass
+ def F(o: A): number
+ return (<B>o).mylen
+ enddef
+
+ defcompile F
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using a variable of type "any" with an object
+def Test_any_obj_var_type()
+ var lines =<< trim END
+ mnv9script
+ class A
+ var name: string = "foobar"
+ def Foo(): string
+ return "func foo"
+ enddef
+ endclass
+
+ def CheckVals(x: any)
+ assert_equal("foobar", x.name)
+ assert_equal("func foo", x.Foo())
+ enddef
+
+ var a = A.new()
+ CheckVals(a)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Try to set a non-existing variable
+ lines =<< trim END
+ mnv9script
+ class A
+ var name: string = "foobar"
+ endclass
+
+ def SetNonExistingVar(x: any)
+ x.bar = [1, 2, 3]
+ enddef
+
+ var a = A.new()
+ SetNonExistingVar(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1326: Variable "bar" not found in object "A"', 1)
+
+ # Try to read a non-existing variable
+ lines =<< trim END
+ mnv9script
+ class A
+ var name: string = "foobar"
+ endclass
+
+ def GetNonExistingVar(x: any)
+ var i: dict<any> = x.bar
+ enddef
+
+ var a = A.new()
+ GetNonExistingVar(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1326: Variable "bar" not found in object "A"', 1)
+
+ # Try to invoke a non-existing method
+ lines =<< trim END
+ mnv9script
+ class A
+ def Foo(): number
+ return 10
+ enddef
+ endclass
+
+ def CallNonExistingMethod(x: any)
+ var i: number = x.Bar()
+ enddef
+
+ var a = A.new()
+ CallNonExistingMethod(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1326: Variable "Bar" not found in object "A"', 1)
+
+ # Use an object which is a Dict value
+ lines =<< trim END
+ mnv9script
+ class Foo
+ def Bar(): number
+ return 369
+ enddef
+ endclass
+
+ def GetValue(FooDict: dict<any>): number
+ var n: number = 0
+ for foo in values(FooDict)
+ n += foo.Bar()
+ endfor
+ return n
+ enddef
+
+ var d = {'x': Foo.new()}
+ assert_equal(369, GetValue(d))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Nested data. Object containing a Dict containing another Object.
+ lines =<< trim END
+ mnv9script
+ class Context
+ public var state: dict<any> = {}
+ endclass
+
+ class Metadata
+ public var value = 0
+ endclass
+
+ var ctx = Context.new()
+ ctx.state["meta"] = Metadata.new(2468)
+
+ const foo = ctx.state.meta.value
+
+ def F(): number
+ const bar = ctx.state.meta.value
+ return bar
+ enddef
+
+ assert_equal(2468, F())
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Accessing an object from a method inside the class using any type
+ lines =<< trim END
+ mnv9script
+ class C
+ def _G(): string
+ return '_G'
+ enddef
+ static def S(o_any: any): string
+ return o_any._G()
+ enddef
+ endclass
+
+ var o1 = C.new()
+ assert_equal('_G', C.S(o1))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Modifying an object protected variable from a method in another class using
+ # any type
+ lines =<< trim END
+ mnv9script
+
+ class A
+ var num = 10
+ endclass
+
+ class B
+ def SetVal(x: any)
+ x.num = 20
+ enddef
+ endclass
+
+ var a = A.new()
+ var b = B.new()
+ b.SetVal(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1335: Variable "num" in class "A" is not writable', 1)
+
+ # Accessing a object protected variable from a method in another class using
+ # any type
+ lines =<< trim END
+ mnv9script
+
+ class A
+ var _num = 10
+ endclass
+
+ class B
+ def GetVal(x: any): number
+ return x._num
+ enddef
+ endclass
+
+ var a = A.new()
+ var b = B.new()
+ var i = b.GetVal(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_num" in class "A"', 1)
+
+ # Accessing an object returned from an imported function and class
+ lines =<< trim END
+ mnv9script
+ export class Foo
+ public var name: string
+ endclass
+
+ export def ReturnFooObject(): Foo
+ var r = Foo.new('star')
+ return r
+ enddef
+ END
+ writefile(lines, 'Xanyvar1.mnv', 'D')
+
+ lines =<< trim END
+ mnv9script
+
+ import './Xanyvar1.mnv'
+
+ def GetName(): string
+ var whatever = Xanyvar1.ReturnFooObject()
+ return whatever.name
+ enddef
+
+ assert_equal('star', GetName())
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Try to modify a protected object variable using a variable of type "any"
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var n: number = 10
+ endclass
+ def Fn(x: any)
+ x.n = 20
+ enddef
+ var a = Foo.new()
+ Fn(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1335: Variable "n" in class "Foo" is not writable', 1)
+
+ # Try to read a protected object variable using a variable of type "any"
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var _n: number = 10
+ endclass
+ def Fn(x: any): number
+ return x._n
+ enddef
+
+ var a = Foo.new()
+ Fn(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_n" in class "Foo"', 1)
+
+ # Read a protected object variable using a variable of type "any" in an object
+ # method
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var _n: number = 10
+ def Fn(x: any): number
+ return x._n
+ enddef
+ endclass
+
+ var a = Foo.new()
+ assert_equal(10, a.Fn(a))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Try to call a protected object method using a "any" type variable
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ def _GetVal(): number
+ return 234
+ enddef
+ endclass
+ def Fn(x: any): number
+ return x._GetVal()
+ enddef
+
+ var a = Foo.new()
+ Fn(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1366: Cannot access protected method: _GetVal', 1)
+
+ # Call a protected object method using a "any" type variable from another
+ # object method
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ def _GetVal(): number
+ return 234
+ enddef
+ def FooVal(x: any): number
+ return x._GetVal()
+ enddef
+ endclass
+
+ var a = Foo.new()
+ assert_equal(234, a.FooVal(a))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Method chaining
+ lines =<< trim END
+ mnv9script
+
+ export class T
+ var id: number = 268
+ def F(): any
+ return this
+ enddef
+ endclass
+
+ def H()
+ var a = T.new().F().F()
+ assert_equal(268, a.id)
+ enddef
+ H()
+
+ var b: T = T.new().F().F()
+ assert_equal(268, b.id)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Using a null object to access a member variable
+ lines =<< trim END
+ mnv9script
+ def Fn(x: any): number
+ return x.num
+ enddef
+
+ Fn(null_object)
+ END
+ v9.CheckScriptFailure(lines, 'E1360: Using a null object', 1)
+
+ # Using a null object to invoke a method
+ lines =<< trim END
+ mnv9script
+ def Fn(x: any)
+ x.Foo()
+ enddef
+
+ Fn(null_object)
+ END
+ v9.CheckScriptFailure(lines, 'E1360: Using a null object', 1)
+
+ # Try to change a const object variable using a "any" variable
+ lines =<< trim END
+ mnv9script
+ class A
+ public const v1: number = 123
+ endclass
+
+ def Fn(o: any)
+ o.v1 = 321
+ enddef
+
+ var a = A.new()
+ Fn(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1409: Cannot change read-only variable "v1" in class "A"', 1)
+
+ # Try to change a final object variable using a "any" variable
+ lines =<< trim END
+ mnv9script
+ class A
+ public final v1: number = 123
+ endclass
+
+ def Fn(o: any)
+ o.v1 = 321
+ enddef
+
+ var a = A.new()
+ Fn(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1409: Cannot change read-only variable "v1" in class "A"', 1)
+
+ # Assign a different type of value to an "any" type object variable
+ lines =<< trim END
+ mnv9script
+ class A
+ public var v1: list<any> = [1, 2]
+ endclass
+
+ def Fn(o: A)
+ o.v1 = 'abc'
+ enddef
+
+ var a = A.new()
+ Fn(a)
+ END
+ v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected list<any> but got string', 1)
+enddef
+
+" Test for using an object method with mapnew()
+def Test_mapnew_with_instance_method()
+ var lines =<< trim END
+ mnv9script
+
+ class Foo
+ var str: string
+ var nums: list<number> = [1, 2, 3]
+
+ def InstanceMethod(n: number): string
+ return this.str .. n
+ enddef
+
+ def MapperMethod(idx: number, elem: number): string
+ return elem->this.InstanceMethod()
+ enddef
+
+ def MapTest()
+ this.str = "foo"
+ var l = ['foo1', 'foo2', 'foo3']
+ assert_equal(l, this.nums->mapnew(this.MapperMethod))
+ enddef
+ endclass
+
+ Foo.new().MapTest()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Error in the mapnew() function
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var str: string
+ var nums: list<number> = [1, 2, 3]
+
+ def InstanceMethod(n: number): string
+ throw "InstanceMethod failed"
+ enddef
+
+ def MapperMethod(idx: number, elem: number): string
+ return elem->this.InstanceMethod()
+ enddef
+
+ def MapTest()
+ this.str = "foo"
+ var caught_exception: bool = false
+ try
+ this.nums->mapnew(this.MapperMethod)
+ catch /InstanceMethod failed/
+ caught_exception = true
+ endtry
+ assert_true(caught_exception)
+ enddef
+ endclass
+
+ Foo.new().MapTest()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an object method in a method call.
+def Test_use_object_method_in_a_method_call()
+ var lines =<< trim END
+ mnv9script
+
+ class Foo
+ def Cost(nums: list<number>): number
+ return nums[0] * nums[1]
+ enddef
+
+ def ShowCost(): string
+ var g = [4, 5]
+ return $"Cost is: {g->this.Cost()}"
+ enddef
+ endclass
+
+ var d = Foo.new()
+ assert_equal('Cost is: 20', d.ShowCost())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for using a non-existing object method in string interpolation
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ def Cost(nums: list<number>): number
+ return nums[0] * nums[1]
+ enddef
+
+ def ShowCost(): string
+ var g = [4, 5]
+ echo $"Cost is: {g->this.NewCost()}"
+ enddef
+ endclass
+
+ var d = Foo.new()
+ d.ShowCost()
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "NewCost" not found in object "Foo"')
+enddef
+
+" Test for referencing an object variable which is not yet initialized
+def Test_uninitialized_object_var()
+ var lines =<< trim END
+ mnv9script
+ class Foo
+ const two: number = Foo.Two(this)
+ const one: number = 1
+
+ static def Two(that: Foo): number
+ return that.one + 2
+ enddef
+ endclass
+
+ echo Foo.Two(Foo.new())
+ END
+ v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'one' referenced")
+
+ lines =<< trim END
+ mnv9script
+ class Foo
+ const one: number = Foo.One(this)
+
+ static def One(that: Foo): number
+ return 1
+ enddef
+ endclass
+
+ assert_equal(1, Foo.One(Foo.new()))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Foo
+ const one: number = 1
+ const two: number = Foo.Two(this)
+
+ static def Two(that: Foo): number
+ return that.one + 1
+ enddef
+ endclass
+
+ assert_equal(2, Foo.Two(Foo.new()))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Foo
+ const Id: func(any): any = ((_) => (v) => v)(this)
+
+ static def Id(that: Foo): func(any): any
+ return that.Id
+ enddef
+ endclass
+
+ assert_equal(5, Foo.Id(Foo.new())(5))
+ assert_equal(7, Foo.new().Id(7))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Foo
+ const Id: func(any): any = ((that) => (_) => that)(this)
+
+ static def Id(that: Foo): func(any): any
+ return that.Id
+ enddef
+ endclass
+
+ const Id0: func(any): any = Foo.Id(Foo.new())
+ const Id1: func(any): any = Foo.new().Id
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Foo
+ const Id: any = Foo.Id(this)
+
+ static def Id(that: Foo): any
+ return that.Id
+ enddef
+ endclass
+
+ const Id2: any = Foo.Id(Foo.new())
+ const Id3: any = Foo.new().Id
+ END
+ v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'Id' referenced")
+
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var x: string = ''
+ var Y: func(): string = () => this.x
+ endclass
+
+ var foo = Foo.new('ok')
+ assert_equal('ok', foo.Y())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class Foo
+ var x: string = this.x
+ endclass
+
+ var foo = Foo.new('ok')
+ END
+ v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'x' referenced")
+enddef
+
+" Test for initializing member variables of compound type in the constructor
+def Test_constructor_init_compound_member_var()
+ var lines =<< trim END
+ mnv9script
+
+ class Foo
+ var v1: string = "aaa"
+ var v2: list<number> = [1, 2]
+ var v3: dict<string> = {a: 'a', b: 'b'}
+ endclass
+
+ class Bar
+ var v4: string = "bbb"
+ var v5: Foo = Foo.new()
+ var v6: list<number> = [1, 2]
+ endclass
+
+ var b: Bar = Bar.new()
+ assert_equal("aaa", b.v5.v1)
+ assert_equal([1, 2], b.v5.v2)
+ assert_equal({a: 'a', b: 'b'}, b.v5.v3)
+ assert_equal("bbb", b.v4)
+ assert_equal([1, 2], b.v6)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a concrete method in an abstract extended class which is
+" further extended
+def Test_abstract_method_across_hierarchy()
+ var lines =<< trim END
+ mnv9script
+
+ abstract class A
+ abstract def Foo(): string
+ endclass
+
+ abstract class B extends A
+ abstract def Bar(): string
+ endclass
+
+ class C extends B
+ def Foo(): string
+ return 'foo'
+ enddef
+
+ def Bar(): string
+ return 'bar'
+ enddef
+ endclass
+
+ def Fn1(a: A): string
+ return a.Foo()
+ enddef
+
+ def Fn2(b: B): string
+ return b.Bar()
+ enddef
+
+ var c = C.new()
+ assert_equal('foo', Fn1(c))
+ assert_equal('bar', Fn2(c))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ abstract class A
+ abstract def Foo(): string
+ endclass
+
+ abstract class B extends A
+ abstract def Bar(): string
+ endclass
+
+ class C extends B
+ def Bar(): string
+ return 'bar'
+ enddef
+ endclass
+
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented')
+
+ lines =<< trim END
+ mnv9script
+
+ abstract class A
+ abstract def M1(): string
+ abstract def M2(): string
+ endclass
+
+ abstract class B extends A
+ def M1(): string
+ return 'B: M1'
+ enddef
+
+ def M2(): string
+ return 'B: M2'
+ enddef
+ endclass
+
+ class C1 extends B
+ def M1(): string
+ return 'C1: M1'
+ enddef
+ endclass
+
+ class C2 extends B
+ def M2(): string
+ return 'C2: M2'
+ enddef
+ endclass
+
+ class D1 extends C1
+ endclass
+
+ class D2 extends C2
+ endclass
+
+ var l: list<string> = []
+ for Type in ['C1', 'C2', 'D1', 'D2']
+ l->add(eval($'{Type}.new().M1()'))
+ l->add(eval($'{Type}.new().M2()'))
+ endfor
+ assert_equal(['C1: M1', 'B: M2', 'B: M1', 'C2: M2', 'C1: M1', 'B: M2', 'B: M1', 'C2: M2'], l)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ abstract class A
+ abstract def M1(): string
+ abstract def M2(): string
+ endclass
+
+ class B extends A
+ def M1(): string
+ return 'B: M1'
+ enddef
+
+ def M2(): string
+ return 'B: M2'
+ enddef
+ endclass
+
+ abstract class C extends B
+ endclass
+
+ class D1 extends C
+ def M1(): string
+ return 'D1: M1'
+ enddef
+ endclass
+
+ class D2 extends C
+ def M2(): string
+ return 'D2: M2'
+ enddef
+ endclass
+
+ class E1 extends D1
+ endclass
+
+ class E2 extends D2
+ endclass
+
+ var l: list<string> = []
+ for Type in ['B', 'D1', 'D2', 'E1', 'E2']
+ l->add(eval($'{Type}.new().M1()'))
+ l->add( eval($'{Type}.new().M2()'))
+ endfor
+ assert_equal(['B: M1', 'B: M2', 'D1: M1', 'B: M2', 'B: M1', 'D2: M2', 'D1: M1', 'B: M2', 'B: M1', 'D2: M2'], l)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a protected new() method (singleton design pattern)
+def Test_protected_new_method()
+ var lines =<< trim END
+ mnv9script
+ class A
+ def _new()
+ enddef
+ endclass
+ var a = A.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "A"', 6)
+
+ lines =<< trim END
+ mnv9script
+ class A
+ static var _instance: A
+ static var handler: func = A._Internal
+ var str: string
+ def _new(str: string)
+ this.str = str
+ enddef
+ static def _Internal(): string
+ return "test"
+ enddef
+ static def GetInstance(str: string): A
+ if _instance == null
+ _instance = A._new(str)
+ endif
+ return _instance
+ enddef
+ endclass
+ var a: A = A.GetInstance('foo')
+ var b: A = A.GetInstance('bar')
+ assert_equal('foo', a.str)
+ assert_equal('foo', b.str)
+ assert_equal('test', A.handler())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ enum Test
+ A,
+ B,
+ C
+
+ static var handler: func = Test._Internal
+
+ static def _Internal(): string
+ return "test"
+ enddef
+ endenum
+ assert_equal('test', Test.handler())
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using 'super' in a closure function inside an object method
+def Test_super_in_closure()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ const _value: number
+
+ def Fn(): func(any): number
+ return (_: any) => this._value
+ enddef
+ endclass
+
+ class B extends A
+ def Fn(): func(any): number
+ return (_: any) => super._value
+ enddef
+ endclass
+
+ assert_equal(100, A.new(100).Fn()(null))
+ assert_equal(200, B.new(200).Fn()(null))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using 'super' to access methods and variables
+def Test_super_keyword()
+ var lines =<< trim END
+ mnv9script
+ class Base
+ var name: string
+ def ToString(): string
+ return this.name
+ enddef
+ endclass
+
+ class Child extends Base
+ var age: number
+ def ToString(): string
+ return super.ToString() .. ': ' .. this.age
+ enddef
+ endclass
+
+ var o = Child.new('John', 42)
+ assert_equal('John: 42', o.ToString())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class Child
+ var age: number
+ def ToString(): string
+ return super .ToString() .. ': ' .. this.age
+ enddef
+ endclass
+ var o = Child.new(42)
+ echo o.ToString()
+ END
+ v9.CheckSourceFailure(lines, 'E1356: "super" must be followed by a dot', 1)
+
+ lines =<< trim END
+ mnv9script
+ class Base
+ var name: string
+ def ToString(): string
+ return this.name
+ enddef
+ endclass
+
+ var age = 42
+ def ToString(): string
+ return super.ToString() .. ': ' .. age
+ enddef
+ echo ToString()
+ END
+ v9.CheckSourceFailure(lines, 'E1357: Using "super" not in a class method', 1)
+
+ lines =<< trim END
+ mnv9script
+ class Child
+ var age: number
+ def ToString(): string
+ return super.ToString() .. ': ' .. this.age
+ enddef
+ endclass
+ var o = Child.new(42)
+ echo o.ToString()
+ END
+ v9.CheckSourceFailure(lines, 'E1358: Using "super" not in a child class', 1)
+
+ # Using super, Child invokes Base method which has optional arg. #12471
+ lines =<< trim END
+ mnv9script
+
+ class Base
+ var success: bool = false
+ def Method(arg = 0)
+ this.success = true
+ enddef
+ endclass
+
+ class Child extends Base
+ def new()
+ super.Method()
+ enddef
+ endclass
+
+ var obj = Child.new()
+ assert_equal(true, obj.success)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using 'super' to access an object variable in the parent
+ lines =<< trim END
+ mnv9script
+
+ class A
+ var foo: string = 'xxx'
+ endclass
+
+ class B extends A
+ def GetString(): string
+ return super.foo
+ enddef
+ endclass
+
+ var b: B = B.new()
+ echo b.GetString()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using 'super' to access an static class variable in the parent should fail
+ lines =<< trim END
+ mnv9script
+
+ class A
+ static var foo: string = 'xxx'
+ endclass
+
+ class B extends A
+ def GetString(): string
+ return super.foo
+ enddef
+ endclass
+
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "foo" not found in object "B"')
+
+ # Using super to access an overridden method in the parent class
+ lines =<< trim END
+ mnv9script
+
+ class A
+ def Foo(): string
+ return 'A.Foo'
+ enddef
+ endclass
+
+ class B extends A
+ def Foo(): string
+ return 'B.Foo'
+ enddef
+
+ def Bar(): string
+ return $'{super.Foo()} {this.Foo()}'
+ enddef
+ endclass
+
+ var b = B.new()
+ assert_equal('A.Foo B.Foo', b.Bar())
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for using super in a lambda function to invoke a base class method from
+ # the new() method.
+ lines =<< trim END
+ mnv9script
+
+ def G(F: func): string
+ return F()
+ enddef
+
+ class Base
+ def F(): string
+ return 'Base.F()'
+ enddef
+ endclass
+
+ class Foo extends Base
+ var s: string = 'x'
+ def new()
+ this.s = G((): string => {
+ return super.F()
+ })
+ enddef
+ endclass
+
+ var f = Foo.new()
+ assert_equal('Base.F()', f.s)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a list of objects
+def Test_method_call_from_list_of_objects()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ var n: list<number> = [100, 101]
+ def F(): string
+ return 'A.F'
+ enddef
+ endclass
+
+ class B
+ var name: string
+ var n: list<number> = [200, 201]
+ def new(this.name)
+ enddef
+ def F(): string
+ return 'B.F'
+ enddef
+ endclass
+
+ var obj1 = A.new()
+ var obj2 = B.new('b1')
+
+ def CheckObjectList()
+ var objlist = [obj1, obj2]
+ assert_equal('list<object<any>>', typename(objlist))
+
+ # Use a member function
+ assert_equal('A.F', objlist[0].F())
+ assert_equal('B.F', objlist[1].F())
+
+ # Use a member variable on the RHS
+ assert_equal([100, 101], objlist[0].n)
+ assert_equal([200, 201], objlist[1].n)
+
+ # Use a member variable on the LHS
+ objlist[0].n[1] = 110
+ objlist[1].n[1] = 210
+ assert_equal([100, 110], objlist[0].n)
+ assert_equal([200, 210], objlist[1].n)
+
+ # Iterate using a for loop
+ var s1 = []
+ for o in objlist
+ add(s1, o.F())
+ endfor
+ assert_equal(['A.F', 'B.F'], s1)
+
+ # Iterate using foreach()
+ var s2 = []
+ foreach(objlist, (k, v) => add(s2, v.F()))
+ assert_equal(['A.F', 'B.F'], s2)
+
+ # Add a new list item
+ objlist->add(B.new('b2'))
+ assert_equal('b2', objlist[2].name)
+ enddef
+
+ CheckObjectList()
+
+ var objlist = [A.new(), B.new('b2')]
+ assert_equal('list<object<any>>', typename(objlist))
+
+ # Use a member function
+ assert_equal('A.F', objlist[0].F())
+ assert_equal('B.F', objlist[1].F())
+
+ # Use a member variable on the RHS
+ assert_equal([100, 101], objlist[0].n)
+ assert_equal([200, 201], objlist[1].n)
+
+ # Use a member variable on the LHS
+ objlist[0].n[1] = 110
+ objlist[1].n[1] = 210
+ assert_equal([100, 110], objlist[0].n)
+ assert_equal([200, 210], objlist[1].n)
+
+ # Iterate using a for loop
+ var s1 = []
+ for o in objlist
+ add(s1, o.F())
+ endfor
+ assert_equal(['A.F', 'B.F'], s1)
+
+ # Iterate using foreach()
+ var s2 = []
+ foreach(objlist, (k, v) => add(s2, v.F()))
+ assert_equal(['A.F', 'B.F'], s2)
+
+ # Add a new list item
+ objlist->add(B.new('b2'))
+ assert_equal('b2', objlist[2].name)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+
+ class B
+ endclass
+
+ var objlist = [A.new(), B.new()]
+ def Fn()
+ objlist->add(10)
+ enddef
+
+ try
+ Fn()
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected object<any> but got number')
+ endtry
+
+ try
+ objlist->add(10)
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected object<any> but got number')
+ endtry
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Adding an enum to a List of objects should fail
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B
+ endclass
+ enum C
+ Red,
+ Green,
+ endenum
+ var items = [A.new(), B.new()]
+ def Fn()
+ items->add(C.Red)
+ enddef
+
+ try
+ Fn()
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected object<any> but got enum<C>')
+ endtry
+
+ try
+ items->add(C.Green)
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected object<any> but got enum<C>')
+ endtry
+
+ var items2 = [C.Red, C.Green]
+ def Fn2()
+ items2->add(A.new())
+ enddef
+ try
+ Fn2()
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected enum<C> but got object<A>')
+ endtry
+
+ try
+ items2->add(B.new())
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected enum<C> but got object<B>')
+ endtry
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a dict of objects
+def Test_dict_of_objects()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ var n: list<number> = [100, 101]
+ def F(): string
+ return 'A.F'
+ enddef
+ endclass
+
+ class B
+ var name: string
+ var n: list<number> = [200, 201]
+ def new(this.name)
+ enddef
+ def F(): string
+ return 'B.F'
+ enddef
+ endclass
+
+ var obj1 = A.new()
+ var obj2 = B.new('b1')
+
+ def CheckObjectDict()
+ var objdict = {o_a: obj1, o_b: obj2}
+ assert_equal('dict<object<any>>', typename(objdict))
+
+ # Use a member function
+ assert_equal('A.F', objdict.o_a.F())
+ assert_equal('B.F', objdict.o_b.F())
+
+ # Use a member variable on the RHS
+ assert_equal([100, 101], objdict.o_a.n)
+ assert_equal([200, 201], objdict.o_b.n)
+
+ # Use a member variable on the LHS
+ objdict.o_a.n[1] = 110
+ objdict.o_b.n[1] = 210
+ assert_equal([100, 110], objdict.o_a.n)
+ assert_equal([200, 210], objdict.o_b.n)
+
+ # Iterate using a for loop
+ var l = []
+ for v in values(objdict)
+ add(l, v.F())
+ endfor
+ assert_equal(['A.F', 'B.F'], l)
+
+ # Iterate using foreach()
+ l = []
+ foreach(objdict, (k, v) => add(l, v.F()))
+ assert_equal(['A.F', 'B.F'], l)
+
+ # Add a new dict item
+ objdict['o_b2'] = B.new('b2')
+ assert_equal('b2', objdict.o_b2.name)
+ enddef
+
+ CheckObjectDict()
+
+ var objdict = {o_a: A.new(), o_b: B.new('b2')}
+ assert_equal('dict<object<any>>', typename(objdict))
+ assert_equal('A.F', objdict.o_a.F())
+ assert_equal('B.F', objdict.o_b.F())
+ assert_equal([100, 101], objdict.o_a.n)
+ assert_equal([200, 201], objdict.o_b.n)
+
+ var l = []
+ for v in values(objdict)
+ add(l, v.F())
+ endfor
+ assert_equal(['A.F', 'B.F'], l)
+
+ # Add a new dict item
+ objdict['o_b2'] = B.new('b2')
+ assert_equal('b2', objdict.o_b2.name)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+ class B
+ endclass
+ class C
+ endclass
+ var objdict = {a: A.new(), b: B.new()}
+ def Fn()
+ objdict['c'] = C.new()
+ enddef
+
+ try
+ Fn()
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected object<any> but got number')
+ endtry
+
+ try
+ objdict['c'] = C.new()
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected object<any> but got number')
+ endtry
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Adding an enum to a Dict of objects should fail
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B
+ endclass
+ enum C
+ Red,
+ Green,
+ endenum
+ var items = {o_a: A.new(), o_b: B.new()}
+ def Fn()
+ items['o_c'] = C.Red
+ enddef
+
+ try
+ Fn()
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected object<any> but got enum<C>')
+ endtry
+
+ try
+ items['o_c'] = C.Green
+ catch
+ assert_exception('MNV(var):E1012: Type mismatch; expected object<any> but got enum<C>')
+ endtry
+
+ var items2 = {red: C.Red, green: C.Green}
+ def Fn2()
+ items2['o_a'] = A.new()
+ enddef
+ try
+ Fn2()
+ catch
+ assert_exception('MNV(eval):E1012: Type mismatch; expected enum<C> but got object<A>')
+ endtry
+
+ try
+ items2['o_a'] = B.new()
+ catch
+ assert_exception('MNV(var):E1012: Type mismatch; expected enum<C> but got object<B>')
+ endtry
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using the type() and typename() functions with a variable of type
+" object
+def Test_type_typename_funcs_with_object_variable()
+ var lines =<< trim END
+ mnv9script
+
+ class A
+ endclass
+
+ class B
+ endclass
+
+ var o1: object<any>
+ assert_equal([13, 'object<any>'], [type(o1), typename(o1)])
+
+ var o2: object<A>
+ assert_equal([13, 'object<any>'], [type(o2), typename(o2)])
+
+ var o3: A
+ assert_equal([13, 'object<any>'], [type(o3), typename(o3)])
+
+ var o4 = A.new()
+ assert_equal([13, 'object<A>'], [type(o4), typename(o4)])
+
+ var l = [A.new(), B.new()]
+ assert_equal([13, 'object<B>'], [type(l[1]), typename(l[1])])
+
+ var d = {o_a: A.new(), o_b: B.new()}
+ assert_equal([13, 'object<B>'], [type(d.o_b), typename(d.o_b)])
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for object<any> type
+def Test_object_any_type()
+ # assigning different objects to variable of type object<any>
+ var lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B
+ endclass
+ var x: object<any>
+ x = A.new()
+ assert_true(instanceof(x, A))
+ x = B.new()
+ assert_true(instanceof(x, B))
+ x = null_object
+ assert_true(instanceof(x, null_class))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use a list of object<any> variable
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B
+ endclass
+ var l: list<object<any>>
+ l->add(A.new())
+ l->add(B.new())
+ assert_true(instanceof(l[0], A))
+ assert_true(instanceof(l[1], B))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using object<any> as a function argument type and the return type
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B
+ endclass
+ def Fn(x: object<any>): object<any>
+ return x
+ enddef
+ assert_true(instanceof(Fn(A.new()), A))
+ assert_true(instanceof(Fn(B.new()), B))
+ END
+
+ # Try assigning a different type of value to a object<any> variable
+ lines =<< trim END
+ var x: object<any> = []
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object<any> but got list<any>', 'E1012: Type mismatch; expected object<any> but got list<any>'])
+
+ # Try assigning a different type of value to a object<any> variable
+ lines =<< trim END
+ var x: object<any>
+ x = 0z10
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object<any> but got blob', 'E1012: Type mismatch; expected object<any> but got blob'])
+
+ # Try adding a different type of value to a list<object<any>> variable
+ lines =<< trim END
+ var x: list<object<any>>
+ x->add({})
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object<any> but got dict<any>', 'E1012: Type mismatch; expected object<any> but got dict<any>'])
+
+ # Try adding a different type of value to a dict<object<any>> variable
+ lines =<< trim END
+ var x: dict<object<any>>
+ x['a'] = {}
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object<any> but got dict<any>', 'E1012: Type mismatch; expected object<any> but got dict<any>'])
+
+ # Error if comparing object with non object when using COMPAREANY instruction
+ lines =<< trim END
+ mnv9script
+
+ enum DirectiveType
+ Unknown,
+ Set
+ endenum
+
+ type PatternSteps = list<any>
+ type Directive = tuple<DirectiveType, PatternSteps>
+
+ def Test(): void
+ var directive: Directive = (DirectiveType.Unknown, ["eq", 1, "hello"])
+
+ if directive[0] == "test"
+ endif
+ enddef
+
+ Test()
+ END
+ v9.CheckSourceFailure(lines, 'E1437: Can only compare Object with Object', 3)
+enddef
+
+" Test for object<{class}> type
+def Test_object_of_class_type()
+ var lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ var x: object<A>
+ x = A.new()
+ assert_true(instanceof(x, A))
+ var y: object<A> = A.new()
+ assert_true(instanceof(y, A))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B
+ endclass
+ var x: object<A>
+ x = B.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<A> but got object<B>')
+
+ lines =<< trim END
+ mnv9script
+ class A
+ endclass
+ class B
+ endclass
+ def Fn(x: object<A>): object<B>
+ return B.new()
+ enddef
+ assert_true(instanceof(Fn(A.new()), B))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ var x: object
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1008: Missing <type> after object', 'E1008: Missing <type> after object'])
+
+ lines =<< trim END
+ var x: object <any>
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1068: No white space allowed before ''<'': <any>', 'E1068: No white space allowed before ''<'': <any>'])
+
+ lines =<< trim END
+ var x: object<any
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1009: Missing > after type: <any', 'E1009: Missing > after type: <any'])
+
+ lines =<< trim END
+ var x: object<any,any>
+ END
+ v9.CheckSourceDefFailure(lines, 'E1009: Missing > after type: <any,any>')
+
+ lines =<< trim END
+ mnv9script
+ var x: object<any,any>
+ END
+ v9.CheckSourceFailure(lines, 'E488: Trailing characters: ,any>')
+
+ lines =<< trim END
+ var x: object<number>
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, [
+ \ 'E1353: Class name not found: <number>',
+ \ 'E1353: Class name not found: <number>'])
+enddef
+
+" Test for the object and class member type
+def Test_obj_class_member_type()
+ var lines =<< trim END
+ mnv9script
+ class L
+ var l: list<number>
+ endclass
+ var obj_L = L.new([10, 20])
+ assert_equal('list<number>', typename(obj_L.l))
+ obj_L.l->add('a')
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got string', 7)
+
+ lines =<< trim END
+ mnv9script
+ class T
+ var t: list<tuple<string>>
+ endclass
+ var obj_T = T.new([('a',), ('b',)])
+ assert_equal('list<tuple<string>>', typename(obj_T.t))
+ obj_T.t->add([('c', 10, true)])
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected tuple<string> but got list<tuple<string, number, bool>>', 7)
+
+ lines =<< trim END
+ mnv9script
+ class D
+ var d: dict<number>
+ endclass
+ var obj_D = D.new({a: 10, b: 20})
+ assert_equal('dict<number>', typename(obj_D.d))
+ obj_D.d->extend({c: 'C'})
+ END
+ v9.CheckSourceFailure(lines, 'E1013: Argument 2: type mismatch, expected dict<number> but got dict<string> in extend()', 7)
+
+ lines =<< trim END
+ mnv9script
+ class L
+ public static var l: list<number>
+ endclass
+ L.l = [10, 20]
+ assert_equal('list<number>', typename(L.l))
+ L.l->add('a')
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got string', 7)
+
+ lines =<< trim END
+ mnv9script
+ class T
+ public static var t: list<tuple<string>>
+ endclass
+ T.t = [('a',), ('b',)]
+ assert_equal('list<tuple<string>>', typename(T.t))
+ T.t->add([('c', 10, true)])
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected tuple<string> but got list<tuple<string, number, bool>>', 7)
+
+ lines =<< trim END
+ mnv9script
+ class D
+ public static var d: dict<number>
+ endclass
+ D.d = {a: 10, b: 20}
+ assert_equal('dict<number>', typename(D.d))
+ D.d->extend({c: 'C'})
+ END
+ v9.CheckSourceFailure(lines, 'E1013: Argument 2: type mismatch, expected dict<number> but got dict<string> in extend()', 7)
+enddef
+
+" Test for garbage collecting a class with a member referring to the class
+" (self reference)
+func Test_class_selfref_gc()
+ let lines =<< trim END
+ mnv9script
+ class Foo
+ static var MyFoo = Foo.new()
+ static var d = {a: [1, 2]}
+ static var l = [{a: 'a', b: 'b'}]
+ endclass
+ assert_equal(2, test_refcount(Foo))
+ test_garbagecollect_now()
+ assert_equal(2, test_refcount(Foo))
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test if class members can be accessed via a lambda inside a object/class
+" method.
+func Test_class_member_lambda()
+ let lines =<< trim END
+ mnv9script
+ class A
+ static var regular: string
+ static var _protected: string
+
+ static def RegularMethod(): string
+ return A.regular
+ enddef
+
+ static def _ProtectedMethod(): string
+ return A._protected
+ enddef
+
+ def new()
+ var FuncA: func = () => {
+ A.regular = "regular"
+ assert_equal("regular", A.RegularMethod())
+ }
+ var FuncB: func = () => {
+ A._protected = "protected"
+ assert_equal("protected", A._ProtectedMethod())
+ }
+
+ FuncA()
+ FuncB()
+ enddef
+ endclass
+
+ A.new()
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" mnv: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker