Mercurial > vim
comparison src/testdir/test_vim9_class.vim @ 33393:016d8f863230 v9.0.1955
patch 9.0.1955: Vim9: lockvar issues with objects/classes
Commit: https://github.com/vim/vim/commit/ee865f37acab6cac2cee6a171d60e1b365f852b0
Author: Ernie Rael <errael@raelity.com>
Date: Fri Sep 29 19:53:55 2023 +0200
patch 9.0.1955: Vim9: lockvar issues with objects/classes
Problem: Vim9: lockvar issues with objects/classes
Solution: fix `get_lhs()` object/class access and avoid `SEGV`,
make error messages more accurate.
- `get_lval()` detects/returns object/class access
- `compile_lock_unlock()` generate code for bare static and obj_arg access
- `do_lock_var()` check lval for `ll_object`/`ll_class` and fail if so.
Details:
- Add `ll_object`/`ll_class`/`ll_oi` to `lval_T`.
- Add `lockunlock_T` to `isn_T` for `is_arg` to specify handling of `lval_root` in `get_lval()`.
- In `get_lval()`, fill in `ll_object`/`ll_class`/`ll_oi` as needed; when no `[idx] or .key`, check lval_root on the way out.
- In `do_lock_var()` check for `ll_object`/`ll_class`; also bullet proof ll_dict case
and give `Dictionay required` if problem. (not needed to avoid lockvar crash anymore)
- In `compile_lock_unlock()` compile for the class variable and func arg cases.
closes: #13174
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Ernie Rael <errael@raelity.com>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Fri, 29 Sep 2023 20:00:07 +0200 |
parents | b5ad84fdc702 |
children | bb99820510ef |
comparison
equal
deleted
inserted
replaced
33392:3d3d0492824e | 33393:016d8f863230 |
---|---|
3478 v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0) | 3478 v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0) |
3479 enddef | 3479 enddef |
3480 | 3480 |
3481 " Test for locking a variable referring to an object and reassigning to another | 3481 " Test for locking a variable referring to an object and reassigning to another |
3482 " object. | 3482 " object. |
3483 def Test_object_lockvar() | 3483 def Test_lockvar_object() |
3484 var lines =<< trim END | 3484 var lines =<< trim END |
3485 vim9script | 3485 vim9script |
3486 | 3486 |
3487 class C | 3487 class C |
3488 this.val: number | 3488 this.val: number |
3509 | 3509 |
3510 F() | 3510 F() |
3511 assert_equal(3, current.val) | 3511 assert_equal(3, current.val) |
3512 G() | 3512 G() |
3513 assert_equal(2, current.val) | 3513 assert_equal(2, current.val) |
3514 END | |
3515 v9.CheckSourceSuccess(lines) | |
3516 enddef | |
3517 | |
3518 " Test trying to lock an object variable from various places | |
3519 def Test_lockvar_object_variable() | |
3520 # An object variable lockvar has several cases: | |
3521 # object method, scriptlevel, scriplevel from :def, :def arg | |
3522 # method arg, static method arg. | |
3523 # Also different depths | |
3524 | |
3525 # TODO: handle inside_class in vim9class | |
3526 # lockvar of a read-only currently fails even if inside | |
3527 | |
3528 # | |
3529 # lockvar of read-only object variable | |
3530 # | |
3531 | |
3532 # read-only lockvar from object method | |
3533 var lines =<< trim END | |
3534 vim9script | |
3535 | |
3536 class C | |
3537 this.val1: number | |
3538 def Lock() | |
3539 lockvar this.val1 | |
3540 enddef | |
3541 endclass | |
3542 var o = C.new(3) | |
3543 o.Lock() | |
3544 END | |
3545 # TODO: wrong error | |
3546 v9.CheckSourceFailure(lines, 'E1335: Variable "val1" in class "C" is not writable') | |
3547 | |
3548 # read-only lockvar from scriptlevel | |
3549 lines =<< trim END | |
3550 vim9script | |
3551 | |
3552 class C | |
3553 this.val2: number | |
3554 endclass | |
3555 var o = C.new(3) | |
3556 lockvar o.val2 | |
3557 END | |
3558 v9.CheckSourceFailure(lines, 'E1335: Variable "val2" in class "C" is not writable') | |
3559 | |
3560 # read-only lockvar of scriptlevel variable from def | |
3561 lines =<< trim END | |
3562 vim9script | |
3563 | |
3564 class C | |
3565 this.val3: number | |
3566 endclass | |
3567 var o = C.new(3) | |
3568 def Lock() | |
3569 lockvar o.val3 | |
3570 enddef | |
3571 Lock() | |
3572 END | |
3573 v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable') | |
3574 | |
3575 # read-only lockvar of def argument variable | |
3576 lines =<< trim END | |
3577 vim9script | |
3578 | |
3579 class C | |
3580 this.val4: number | |
3581 endclass | |
3582 def Lock(o: C) | |
3583 lockvar o.val4 | |
3584 enddef | |
3585 Lock(C.new(3)) | |
3586 END | |
3587 v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable') | |
3588 | |
3589 # TODO: the following tests use type "any" for argument. Need a run time | |
3590 # check for access. Probably OK as is for now. | |
3591 | |
3592 # read-only lockvar from object method arg | |
3593 lines =<< trim END | |
3594 vim9script | |
3595 | |
3596 class C | |
3597 this.val5: number | |
3598 def Lock(o_any: any) | |
3599 lockvar o_any.val5 | |
3600 enddef | |
3601 endclass | |
3602 var o = C.new(3) | |
3603 o.Lock(C.new(5)) | |
3604 END | |
3605 # TODO: wrong error, tricky since type "any" | |
3606 v9.CheckSourceFailure(lines, 'E1335: Variable "val5" in class "C" is not writable') | |
3607 | |
3608 # read-only lockvar from class method arg | |
3609 lines =<< trim END | |
3610 vim9script | |
3611 | |
3612 class C | |
3613 this.val6: number | |
3614 static def Lock(o_any: any) | |
3615 lockvar o_any.val6 | |
3616 enddef | |
3617 endclass | |
3618 var o = C.new(3) | |
3619 C.Lock(o) | |
3620 END | |
3621 # TODO: wrong error, tricky since type "any" | |
3622 v9.CheckSourceFailure(lines, 'E1335: Variable "val6" in class "C" is not writable') | |
3623 | |
3624 # | |
3625 # lockvar of public object variable | |
3626 # | |
3627 | |
3628 # lockvar from object method | |
3629 lines =<< trim END | |
3630 vim9script | |
3631 | |
3632 class C | |
3633 public this.val1: number | |
3634 def Lock() | |
3635 lockvar this.val1 | |
3636 enddef | |
3637 endclass | |
3638 var o = C.new(3) | |
3639 o.Lock() | |
3640 END | |
3641 v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1) | |
3642 | |
3643 # lockvar from scriptlevel | |
3644 lines =<< trim END | |
3645 vim9script | |
3646 | |
3647 class C | |
3648 public this.val2: number | |
3649 endclass | |
3650 var o = C.new(3) | |
3651 lockvar o.val2 | |
3652 END | |
3653 v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val2" in class "C"', 7) | |
3654 | |
3655 # lockvar of scriptlevel variable from def | |
3656 lines =<< trim END | |
3657 vim9script | |
3658 | |
3659 class C | |
3660 public this.val3: number | |
3661 endclass | |
3662 var o = C.new(3) | |
3663 def Lock() | |
3664 lockvar o.val3 | |
3665 enddef | |
3666 Lock() | |
3667 END | |
3668 v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1) | |
3669 | |
3670 # lockvar of def argument variable | |
3671 lines =<< trim END | |
3672 vim9script | |
3673 | |
3674 class C | |
3675 public this.val4: number | |
3676 endclass | |
3677 def Lock(o: C) | |
3678 lockvar o.val4 | |
3679 enddef | |
3680 Lock(C.new(3)) | |
3681 END | |
3682 v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val4" in class "C"', 1) | |
3683 | |
3684 # lockvar from object method arg | |
3685 lines =<< trim END | |
3686 vim9script | |
3687 | |
3688 class C | |
3689 public this.val5: number | |
3690 def Lock(o_any: any) | |
3691 lockvar o_any.val5 | |
3692 enddef | |
3693 endclass | |
3694 var o = C.new(3) | |
3695 o.Lock(C.new(5)) | |
3696 END | |
3697 v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val5" in class "C"', 1) | |
3698 | |
3699 # lockvar from class method arg | |
3700 lines =<< trim END | |
3701 vim9script | |
3702 | |
3703 class C | |
3704 public this.val6: number | |
3705 static def Lock(o_any: any) | |
3706 lockvar o_any.val6 | |
3707 enddef | |
3708 endclass | |
3709 var o = C.new(3) | |
3710 C.Lock(o) | |
3711 END | |
3712 v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val6" in class "C"', 1) | |
3713 enddef | |
3714 | |
3715 " Test trying to lock a class variable from various places | |
3716 def Test_lockvar_class_variable() | |
3717 | |
3718 # lockvar bare static from object method | |
3719 var lines =<< trim END | |
3720 vim9script | |
3721 | |
3722 class C | |
3723 public static sval1: number | |
3724 def Lock() | |
3725 lockvar sval1 | |
3726 enddef | |
3727 endclass | |
3728 var o = C.new() | |
3729 o.Lock() | |
3730 END | |
3731 v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1) | |
3732 | |
3733 # lockvar C.static from object method | |
3734 lines =<< trim END | |
3735 vim9script | |
3736 | |
3737 class C | |
3738 public static sval2: number | |
3739 def Lock() | |
3740 lockvar C.sval2 | |
3741 enddef | |
3742 endclass | |
3743 var o = C.new() | |
3744 o.Lock() | |
3745 END | |
3746 v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1) | |
3747 | |
3748 # lockvar bare static from class method | |
3749 lines =<< trim END | |
3750 vim9script | |
3751 | |
3752 class C | |
3753 public static sval3: number | |
3754 static def Lock() | |
3755 lockvar sval3 | |
3756 enddef | |
3757 endclass | |
3758 C.Lock() | |
3759 END | |
3760 v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1) | |
3761 | |
3762 # lockvar C.static from class method | |
3763 lines =<< trim END | |
3764 vim9script | |
3765 | |
3766 class C | |
3767 public static sval4: number | |
3768 static def Lock() | |
3769 lockvar C.sval4 | |
3770 enddef | |
3771 endclass | |
3772 C.Lock() | |
3773 END | |
3774 v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1) | |
3775 | |
3776 # lockvar C.static from script level | |
3777 lines =<< trim END | |
3778 vim9script | |
3779 | |
3780 class C | |
3781 public static sval5: number | |
3782 endclass | |
3783 lockvar C.sval5 | |
3784 END | |
3785 v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6) | |
3786 | |
3787 # lockvar o.static from script level | |
3788 lines =<< trim END | |
3789 vim9script | |
3790 | |
3791 class C | |
3792 public static sval6: number | |
3793 endclass | |
3794 var o = C.new() | |
3795 lockvar o.sval6 | |
3796 END | |
3797 v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7) | |
3798 enddef | |
3799 | |
3800 " Test locking an argument to :def | |
3801 def Test_lockvar_argument() | |
3802 # Lockvar a function arg | |
3803 var lines =<< trim END | |
3804 vim9script | |
3805 | |
3806 def Lock(val: any) | |
3807 lockvar val | |
3808 enddef | |
3809 | |
3810 var d = {a: 1, b: 2} | |
3811 Lock(d) | |
3812 | |
3813 d->extend({c: 3}) | |
3814 END | |
3815 v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument') | |
3816 | |
3817 # Lockvar a function arg. Verify "sval" is interpreted as argument and not a | |
3818 # class member in "C". This tests lval_root_is_arg. | |
3819 lines =<< trim END | |
3820 vim9script | |
3821 | |
3822 class C | |
3823 public static sval: list<number> | |
3824 endclass | |
3825 | |
3826 def Lock2(sval: any) | |
3827 lockvar sval | |
3828 enddef | |
3829 | |
3830 var o = C.new() | |
3831 Lock2(o) | |
3832 END | |
3833 v9.CheckSourceSuccess(lines) | |
3834 | |
3835 # Lock a class. | |
3836 lines =<< trim END | |
3837 vim9script | |
3838 | |
3839 class C | |
3840 public static sval: list<number> | |
3841 endclass | |
3842 | |
3843 def Lock2(sval: any) | |
3844 lockvar sval | |
3845 enddef | |
3846 | |
3847 Lock2(C) | |
3848 END | |
3849 v9.CheckSourceSuccess(lines) | |
3850 | |
3851 # Lock an object. | |
3852 lines =<< trim END | |
3853 vim9script | |
3854 | |
3855 class C | |
3856 public static sval: list<number> | |
3857 endclass | |
3858 | |
3859 def Lock2(sval: any) | |
3860 lockvar sval | |
3861 enddef | |
3862 | |
3863 Lock2(C.new()) | |
3864 END | |
3865 v9.CheckSourceSuccess(lines) | |
3866 | |
3867 # In this case (unlike previous) "lockvar sval" is a class member. | |
3868 lines =<< trim END | |
3869 vim9script | |
3870 | |
3871 class C | |
3872 public static sval: list<number> | |
3873 def Lock2() | |
3874 lockvar sval | |
3875 enddef | |
3876 endclass | |
3877 | |
3878 | |
3879 var o = C.new() | |
3880 o.Lock2() | |
3881 END | |
3882 v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1) | |
3883 enddef | |
3884 | |
3885 " Test that this can be locked without error | |
3886 def Test_lockvar_this() | |
3887 # lockvar this | |
3888 var lines =<< trim END | |
3889 vim9script | |
3890 class C | |
3891 def TLock() | |
3892 lockvar this | |
3893 enddef | |
3894 endclass | |
3895 var o = C.new() | |
3896 o.TLock() | |
3897 END | |
3898 v9.CheckSourceSuccess(lines) | |
3899 | |
3900 # lockvar four (four letter word, but not this) | |
3901 lines =<< trim END | |
3902 vim9script | |
3903 class C | |
3904 def TLock4() | |
3905 var four: number | |
3906 lockvar four | |
3907 enddef | |
3908 endclass | |
3909 var o = C.new() | |
3910 o.TLock4() | |
3911 END | |
3912 v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') | |
3913 | |
3914 # lockvar this5; "this" + one char, 5 letter word, starting with "this" | |
3915 lines =<< trim END | |
3916 vim9script | |
3917 class C | |
3918 def TLock5() | |
3919 var this5: number | |
3920 lockvar this5 | |
3921 enddef | |
3922 endclass | |
3923 var o = C.new() | |
3924 o.TLock5() | |
3925 END | |
3926 v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') | |
3927 enddef | |
3928 | |
3929 " Test some general lockvar cases | |
3930 def Test_lockvar_general() | |
3931 # lockvar an object and a class. It does nothing | |
3932 var lines =<< trim END | |
3933 vim9script | |
3934 class C | |
3935 endclass | |
3936 var o = C.new() | |
3937 lockvar o | |
3938 lockvar C | |
3939 END | |
3940 v9.CheckSourceSuccess(lines) | |
3941 | |
3942 # Lock a list element that's nested in an object variable from a :def | |
3943 lines =<< trim END | |
3944 vim9script | |
3945 | |
3946 class C | |
3947 public this.val: list<list<number>> = [ [1], [2], [3] ] | |
3948 endclass | |
3949 def Lock2(obj: any) | |
3950 lockvar obj.val[1] | |
3951 enddef | |
3952 | |
3953 var o = C.new() | |
3954 Lock2(o) | |
3955 o.val[0] = [9] | |
3956 assert_equal([ [9], [2], [3] ], o.val) | |
3957 try | |
3958 o.val[1] = [999] | |
3959 call assert_false(true, 'assign should have failed') | |
3960 catch | |
3961 assert_exception('E741:') | |
3962 endtry | |
3963 o.val[2] = [8] | |
3964 assert_equal([ [9], [2], [8] ], o.val) | |
3965 END | |
3966 v9.CheckSourceSuccess(lines) | |
3967 | |
3968 # Lock a list element that's nested in an object variable from scriptlevel | |
3969 lines =<< trim END | |
3970 vim9script | |
3971 | |
3972 class C | |
3973 public this.val: list<list<number>> = [ [1], [2], [3] ] | |
3974 endclass | |
3975 | |
3976 var o = C.new() | |
3977 lockvar o.val[1] | |
3978 o.val[0] = [9] | |
3979 assert_equal([ [9], [2], [3] ], o.val) | |
3980 try | |
3981 o.val[1] = [999] | |
3982 call assert_false(true, 'assign should have failed') | |
3983 catch | |
3984 assert_exception('E741:') | |
3985 endtry | |
3986 o.val[2] = [8] | |
3987 assert_equal([ [9], [2], [8] ], o.val) | |
3514 END | 3988 END |
3515 v9.CheckSourceSuccess(lines) | 3989 v9.CheckSourceSuccess(lines) |
3516 enddef | 3990 enddef |
3517 | 3991 |
3518 " Test for a private object method | 3992 " Test for a private object method |