]> granicus.if.org Git - vim/commitdiff
patch 9.0.1041: cannot define a method in a class v9.0.1041
authorBram Moolenaar <Bram@vim.org>
Fri, 9 Dec 2022 21:41:48 +0000 (21:41 +0000)
committerBram Moolenaar <Bram@vim.org>
Fri, 9 Dec 2022 21:41:48 +0000 (21:41 +0000)
Problem:    Cannot define a method in a class.
Solution:   Implement defining an object method.  Make calling an object
            method work.

17 files changed:
src/errors.h
src/eval.c
src/proto/userfunc.pro
src/proto/vim9execute.pro
src/proto/vim9instr.pro
src/structs.h
src/testdir/test_vim9_class.vim
src/userfunc.c
src/version.c
src/vim.h
src/vim9.h
src/vim9class.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c
src/vim9type.c

index 6230527a701b35fb72ed09d56341320c8fad1e00..13d947b13203d8a29bc3d4a4d2606f0d562dffb2 100644 (file)
@@ -3370,4 +3370,6 @@ EXTERN char e_method_not_found_on_class_str_str[]
        INIT(= N_("E1325: Method not found on class \"%s\": %s"));
 EXTERN char e_member_not_found_on_object_str_str[]
        INIT(= N_("E1326: Member not found on object \"%s\": %s"));
+EXTERN char e_object_required_found_str[]
+       INIT(= N_("E1327: Object required, found %s"));
 #endif
index 703e1ea5292e4f4c6cba7d50b3d995c67284451c..0a0a1e11d66e58e1f1702ab2aa9d063395fc3224 100644 (file)
@@ -295,7 +295,7 @@ eval_expr_typval(
 
            // Shortcut to call a compiled function with minimal overhead.
            r = call_def_function(partial->pt_func, argc, argv,
-                                         DEF_USE_PT_ARGV, partial, fc, rettv);
+                                   DEF_USE_PT_ARGV, partial, NULL, fc, rettv);
            if (fc_arg == NULL)
                remove_funccal();
            if (r == FAIL)
index 5c865ce8ded2820ebff663247f78307a93c8b3c9..e3470f68c7fa087410ddc77197b0d773150ecbcc 100644 (file)
@@ -46,7 +46,7 @@ char_u *get_scriptlocal_funcname(char_u *funcname);
 char_u *alloc_printable_func_name(char_u *fname);
 char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
 void list_functions(regmatch_T *regmatch);
-ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, class_T *class_arg);
+ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int in_class);
 void ex_function(exarg_T *eap);
 ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
 void ex_defcompile(exarg_T *eap);
index 81f762ec5cb5bc82334d47f401d35ef84d2ec0cd..7f8e5fd306d18ea1783b02d1b104a4a1da8c5c0c 100644 (file)
@@ -9,7 +9,7 @@ void restore_current_ectx(ectx_T *ectx);
 int add_defer_function(char_u *name, int argcount, typval_T *argvars);
 char_u *char_from_string(char_u *str, varnumber_T index);
 char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
-int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *loopvarinfo, ectx_T *ectx);
+int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *lvi, ectx_T *ectx);
 int may_load_script(int sid, int *loaded);
 typval_T *lookup_debug_var(char_u *name);
 int may_break_in_function(ufunc_T *ufunc);
@@ -17,7 +17,7 @@ int loopvars_check_refcount(loopvars_T *loopvars);
 int set_ref_in_loopvars(int copyID);
 int exe_typval_instr(typval_T *tv, typval_T *rettv);
 char_u *exe_substitute_instr(void);
-int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, funccall_T *funccal, typval_T *rettv);
+int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, object_T *object, funccall_T *funccal, typval_T *rettv);
 void unwind_def_callstack(ectx_T *ectx);
 void may_invoke_defer_funcs(ectx_T *ectx);
 void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);
index 04d76822fe18f299ff9201dad456bbbe10a3aa09..d0b0d86a39458c6d81ce2a56df8227198df0ded5 100644 (file)
@@ -4,6 +4,7 @@ isn_T *generate_instr_drop(cctx_T *cctx, isntype_T isn_type, int drop);
 isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type);
 isn_T *generate_instr_debug(cctx_T *cctx);
 int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
+int generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
 int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
 int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type);
 vartype_T operator_type(type_T *type1, type_T *type2);
index 09b0743886735a54655665eb9386cb9ac586e9ff..1395a202781afd9d4f8f3454e32c9ef1aa008e81 100644 (file)
@@ -1478,15 +1478,16 @@ struct class_S
 
     int                class_obj_method_count;
     ufunc_T    **class_obj_methods;    // allocated
-    ufunc_T    *class_new_func;        // new() function that was created
 
     garray_T   class_type_list;        // used for type pointers
     type_T     class_type;
+    type_T     class_object_type;      // same as class_type but VAR_OBJECT
 };
 
 // Used for v_object of typval of VAR_OBJECT.
 // The member variables follow in an array of typval_T.
-struct object_S {
+struct object_S
+{
     class_T    *obj_class;         // class this object is created for;
                                    // pointer adds to class_refcount
     int                obj_refcount;
@@ -2123,6 +2124,7 @@ typedef struct {
     int                fe_evaluate;    // actually evaluate expressions
     partial_T  *fe_partial;    // for extra arguments
     dict_T     *fe_selfdict;   // Dictionary for "self"
+    object_T   *fe_object;     // object, e.g. for "this.Func()"
     typval_T   *fe_basetv;     // base for base->method()
     type_T     *fe_check_type; // type from funcref or NULL
     int                fe_found_var;   // if the function is not found then give an
index 1945db1c2b01f92e446bb12366c5396aa9aa4fa8..2ed83085816521fbdf8ff25cc719020e3242e011 100644 (file)
@@ -130,12 +130,19 @@ def Test_class_basic()
       class TextPosition
         this.lnum: number
        this.col: number
+
+        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())
   END
   v9.CheckScriptSuccess(lines)
 enddef
index 5db0b709c2a67493c2a9b1bc839695d56060865e..ac7c4fde512dc2033dce53a99a40b16946e385b2 100644 (file)
@@ -188,6 +188,7 @@ get_function_line(
     if (theline != NULL)
     {
        if (lines_to_free->ga_len > 0
+               && eap->cmdlinep != NULL
                && *eap->cmdlinep == ((char_u **)lines_to_free->ga_data)
                                                   [lines_to_free->ga_len - 1])
            *eap->cmdlinep = theline;
@@ -214,7 +215,7 @@ get_function_args(
     garray_T   *default_args,
     int                skip,
     exarg_T    *eap,           // can be NULL
-    class_T    *class_arg,
+    int                in_class,       // TRUE when inside a class
     garray_T   *newlines,      // function body lines
     garray_T   *lines_to_free)
 {
@@ -294,7 +295,7 @@ get_function_args(
                }
            }
        }
-       else if (class_arg != NULL && STRNCMP(p, "this.", 5) == 0)
+       else if (in_class && STRNCMP(p, "this.", 5) == 0)
        {
            // this.memberName
            p += 5;
@@ -1436,7 +1437,7 @@ get_lambda_tv(
     s = *arg + 1;
     ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
            types_optional ? &argtypes : NULL, types_optional, evalarg,
-                           NULL, &default_args, TRUE, NULL, NULL, NULL, NULL);
+                          NULL, &default_args, TRUE, NULL, FALSE, NULL, NULL);
     if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
     {
        if (types_optional)
@@ -1453,7 +1454,7 @@ get_lambda_tv(
     ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
            types_optional ? &argtypes : NULL, types_optional, evalarg,
                                            &varargs, &default_args,
-                                           FALSE, NULL, NULL, NULL, NULL);
+                                           FALSE, NULL, FALSE, NULL, NULL);
     if (ret == FAIL
                  || (s = skip_arrow(*arg, equal_arrow, &ret_type,
                equal_arrow || vim9script ? &white_error : NULL)) == NULL)
@@ -2733,8 +2734,8 @@ call_user_func(
            profile_may_start_func(&profile_info, fp, caller);
 #endif
        sticky_cmdmod_flags = 0;
-       call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial,
-                                                                   fc, rettv);
+       call_def_function(fp, argcount, argvars, 0,
+                          funcexe->fe_partial, funcexe->fe_object, fc, rettv);
        funcdepth_decrement();
 #ifdef FEAT_PROFILE
        if (do_profiling == PROF_YES && (fp->uf_profiling
@@ -3957,6 +3958,7 @@ list_func_head(ufunc_T *fp, int indent)
  * "is_global" is NULL.
  * flags:
  * TFN_INT:        internal function name OK
+ * TFN_IN_CLASS:    function in a class
  * TFN_QUIET:      be quiet
  * TFN_NO_AUTOLOAD: do not use script autoloading
  * TFN_NO_DEREF:    do not dereference a Funcref
@@ -4172,8 +4174,9 @@ trans_function_name(
     }
 
     // In Vim9 script a user function is script-local by default, unless it
-    // starts with a lower case character: dict.func().
-    vim9_local = ASCII_ISUPPER(*start) && vim9script;
+    // starts with a lower case character: dict.func().  Or when in a class.
+    vim9_local = ASCII_ISUPPER(*start) && vim9script
+                                               && (flags & TFN_IN_CLASS) == 0;
 
     /*
      * Copy the function name to allocated memory.
@@ -4211,8 +4214,10 @@ trans_function_name(
                lead += (int)STRLEN(sid_buf);
        }
     }
-    else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len)
-                                  || (vim9script && *lv.ll_name == '_')))
+    else if (!(flags & TFN_INT)
+           && (builtin_function(lv.ll_name, len)
+                                  || (vim9script && *lv.ll_name == '_'))
+           && !((flags & TFN_IN_CLASS) && STRNCMP(lv.ll_name, "new", 3) == 0))
     {
        semsg(_(vim9script ? e_function_name_must_start_with_capital_str
                           : e_function_name_must_start_with_capital_or_s_str),
@@ -4417,7 +4422,7 @@ list_functions(regmatch_T *regmatch)
  * When "name_arg" is not NULL this is a nested function, using "name_arg" for
  * the function name.
  * "lines_to_free" is a list of strings to be freed later.
- * If "class_arg" is not NULL then the function is defined in this class.
+ * If "in_class" is TRUE then the function is defined inside a class.
  * Returns a pointer to the function or NULL if no function defined.
  */
     ufunc_T *
@@ -4425,7 +4430,7 @@ define_function(
        exarg_T     *eap,
        char_u      *name_arg,
        garray_T    *lines_to_free,
-       class_T     *class_arg)
+       int         in_class)
 {
     int                j;
     int                c;
@@ -4500,7 +4505,7 @@ define_function(
 
     /*
      * Get the function name.  There are these situations:
-     * func        normal function name
+     * func        normal function name, also when "in_class" is TRUE
      *             "name" == func, "fudi.fd_dict" == NULL
      * dict.func    new dictionary entry
      *             "name" == NULL, "fudi.fd_dict" set,
@@ -4541,7 +4546,7 @@ define_function(
        }
 
        int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC
-                                             | (class_arg == 0 ? 0 : TFN_INT);
+                                              | (in_class ? TFN_IN_CLASS : 0);
        name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi);
        paren = (vim_strchr(p, '(') != NULL);
        if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
@@ -4743,7 +4748,7 @@ define_function(
     if (get_function_args(&p, ')', &newargs,
                        eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
                         NULL, &varargs, &default_args, eap->skip,
-                        eap, class_arg, &newlines, lines_to_free) == FAIL)
+                        eap, in_class, &newlines, lines_to_free) == FAIL)
        goto errret_2;
     whitep = p;
 
@@ -4860,7 +4865,35 @@ define_function(
     /*
      * If there are no errors, add the function
      */
-    if (fudi.fd_dict == NULL)
+    if (fudi.fd_dict != NULL)
+    {
+       char    numbuf[20];
+
+       fp = NULL;
+       if (fudi.fd_newkey == NULL && !eap->forceit)
+       {
+           emsg(_(e_dictionary_entry_already_exists));
+           goto erret;
+       }
+       if (fudi.fd_di == NULL)
+       {
+           // Can't add a function to a locked dictionary
+           if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
+               goto erret;
+       }
+           // Can't change an existing function if it is locked
+       else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
+           goto erret;
+
+       // Give the function a sequential number.  Can only be used with a
+       // Funcref!
+       vim_free(name);
+       sprintf(numbuf, "%d", ++func_nr);
+       name = vim_strsave((char_u *)numbuf);
+       if (name == NULL)
+           goto erret;
+    }
+    else if (!in_class)
     {
        hashtab_T       *ht;
        char_u          *find_name = name;
@@ -4967,34 +5000,6 @@ define_function(
            }
        }
     }
-    else
-    {
-       char    numbuf[20];
-
-       fp = NULL;
-       if (fudi.fd_newkey == NULL && !eap->forceit)
-       {
-           emsg(_(e_dictionary_entry_already_exists));
-           goto erret;
-       }
-       if (fudi.fd_di == NULL)
-       {
-           // Can't add a function to a locked dictionary
-           if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
-               goto erret;
-       }
-           // Can't change an existing function if it is locked
-       else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
-           goto erret;
-
-       // Give the function a sequential number.  Can only be used with a
-       // Funcref!
-       vim_free(name);
-       sprintf(numbuf, "%d", ++func_nr);
-       name = vim_strsave((char_u *)numbuf);
-       if (name == NULL)
-           goto erret;
-    }
 
     if (fp == NULL)
     {
@@ -5113,7 +5118,8 @@ define_function(
            hi = hash_find(&func_hashtab, name);
            hi->hi_key = UF2HIKEY(fp);
        }
-       else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL)
+       else if (!in_class && hash_add(&func_hashtab,
+                                        UF2HIKEY(fp), "add function") == FAIL)
        {
            free_fp = TRUE;
            goto erret;
@@ -5198,7 +5204,7 @@ ex_function(exarg_T *eap)
     garray_T lines_to_free;
 
     ga_init2(&lines_to_free, sizeof(char_u *), 50);
-    (void)define_function(eap, NULL, &lines_to_free, NULL);
+    (void)define_function(eap, NULL, &lines_to_free, FALSE);
     ga_clear_strings(&lines_to_free);
 }
 
index 5e0bf4fe3d41e62ef3278d64114f458e69d87f4d..10d93134102b35b8993f7a99329bcbed7d004f22 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1041,
 /**/
     1040,
 /**/
index e241da0affb624097ed0b7199c47e60fc513f30f..f197fda96074d1db9f790f0ab8435f8577914461 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2676,7 +2676,8 @@ typedef enum {
 #define TFN_NO_DECL    0x20    // only used for GLV_NO_DECL
 #define TFN_COMPILING  0x40    // only used for GLV_COMPILING
 #define TFN_NEW_FUNC   0x80    // defining a new function
-#define TFN_ASSIGN_WITH_OP     0x100   // only for GLV_ASSIGN_WITH_OP
+#define TFN_ASSIGN_WITH_OP 0x100  // only for GLV_ASSIGN_WITH_OP
+#define TFN_IN_CLASS   0x200   // function in a class
 
 // Values for get_lval() flags argument:
 #define GLV_QUIET      TFN_QUIET       // no error messages
index 96ab40e3b38a22892d41db36d87280e1f3e454a0..91ff6caa38d955bb103fa9b493c46cf55bf46404 100644 (file)
@@ -33,6 +33,7 @@ typedef enum {
     ISN_SOURCE,            // source autoload script, isn_arg.number is the script ID
     ISN_INSTR,     // instructions compiled from expression
     ISN_CONSTRUCT,  // construct an object, using contstruct_T
+    ISN_OBJ_MEMBER, // object member, index is isn_arg.number
 
     // get and set variables
     ISN_LOAD,      // push local variable isn_arg.number
index c2a17e12796e3b10c1a1d8ed83f36a4124697529..395d83a1207f02a7e004e7be2a40682d367a8132 100644 (file)
@@ -77,7 +77,7 @@ ex_class(exarg_T *eap)
 
     // Growarray with object methods declared in the class.
     garray_T objmethods;
-    ga_init2(&objmethods, sizeof(ufunc_T), 10);
+    ga_init2(&objmethods, sizeof(ufunc_T *), 10);
 
     /*
      * Go over the body of the class until "endclass" is found.
@@ -97,22 +97,6 @@ ex_class(exarg_T *eap)
        //        static varname
        //        public static varname
        //        static _varname
-       //
-       // constructors:
-       //        def new()
-       //        enddef
-       //        def newOther()
-       //        enddef
-       //
-       // methods (object, class, generics):
-       //        def someMethod()
-       //        enddef
-       //        static def someMethod()
-       //        enddef
-       //        def <Tval> someMethod()
-       //        enddef
-       //        static def <Tval> someMethod()
-       //        enddef
 
        char_u *p = line;
        if (checkforcmd(&p, "endclass", 4))
@@ -172,6 +156,45 @@ ex_class(exarg_T *eap)
            ++objmembers.ga_len;
        }
 
+       // constructors:
+       //        def new()
+       //        enddef
+       //        def newOther()
+       //        enddef
+       // methods:
+       //        def someMethod()
+       //        enddef
+       // TODO:
+       //        static def someMethod()
+       //        enddef
+       //        def <Tval> someMethod()
+       //        enddef
+       //        static def <Tval> someMethod()
+       //        enddef
+       else if (checkforcmd(&p, "def", 3))
+       {
+           exarg_T     ea;
+           garray_T    lines_to_free;
+
+           CLEAR_FIELD(ea);
+           ea.cmd = line;
+           ea.arg = p;
+           ea.cmdidx = CMD_def;
+           ea.getline = eap->getline;
+           ea.cookie = eap->cookie;
+
+           ga_init2(&lines_to_free, sizeof(char_u *), 50);
+           ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE);
+           ga_clear_strings(&lines_to_free);
+
+           // TODO: how about errors?
+           if (uf != NULL && ga_grow(&objmethods, 1) == OK)
+           {
+               ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
+               ++objmethods.ga_len;
+           }
+       }
+
        else
        {
            semsg(_(e_not_valid_command_in_class_str), line);
@@ -206,7 +229,7 @@ ex_class(exarg_T *eap)
 
        int have_new = FALSE;
        for (int i = 0; i < objmethods.ga_len; ++i)
-           if (STRCMP((((ufunc_T *)objmethods.ga_data) + i)->uf_name,
+           if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
                                                                   "new") == 0)
            {
                have_new = TRUE;
@@ -237,7 +260,7 @@ ex_class(exarg_T *eap)
            garray_T lines_to_free;
            ga_init2(&lines_to_free, sizeof(char_u *), 50);
 
-           ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, cl);
+           ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE);
 
            ga_clear_strings(&lines_to_free);
            vim_free(fga.ga_data);
@@ -248,7 +271,6 @@ ex_class(exarg_T *eap)
                ++objmethods.ga_len;
 
                nf->uf_flags |= FC_NEW;
-               nf->uf_class = cl;
                nf->uf_ret_type = get_type_ptr(&type_list);
                if (nf->uf_ret_type != NULL)
                {
@@ -257,7 +279,6 @@ ex_class(exarg_T *eap)
                    nf->uf_ret_type->tt_argcount = 0;
                    nf->uf_ret_type->tt_args = NULL;
                }
-               cl->class_new_func = nf;
            }
        }
 
@@ -275,8 +296,18 @@ ex_class(exarg_T *eap)
                                        sizeof(ufunc_T *) * objmethods.ga_len);
        vim_free(objmethods.ga_data);
 
+       // Set the class pointer on all the object methods.
+       for (int i = 0; i < objmethods.ga_len; ++i)
+       {
+           ufunc_T *fp = cl->class_obj_methods[i];
+           fp->uf_class = cl;
+           fp->uf_flags |= FC_OBJECT;  // TODO: not for class method
+       }
+
        cl->class_type.tt_type = VAR_CLASS;
        cl->class_type.tt_member = (type_T *)cl;
+       cl->class_object_type.tt_type = VAR_OBJECT;
+       cl->class_object_type.tt_member = (type_T *)cl;
        cl->class_type_list = type_list;
 
        // TODO:
@@ -305,6 +336,11 @@ cleanup:
     }
     ga_clear(&objmembers);
 
+    for (int i = 0; i < objmethods.ga_len; ++i)
+    {
+       ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
+       func_clear_free(uf, FALSE);
+    }
     ga_clear(&objmethods);
     clear_type_list(&type_list);
 }
@@ -419,6 +455,11 @@ class_object_index(
                funcexe_T   funcexe;
                CLEAR_FIELD(funcexe);
                funcexe.fe_evaluate = TRUE;
+               if (rettv->v_type == VAR_OBJECT)
+               {
+                   funcexe.fe_object = rettv->vval.v_object;
+                   ++funcexe.fe_object->obj_refcount;
+               }
 
                // Clear the class or object after calling the function, in
                // case the refcount is one.
@@ -426,7 +467,6 @@ class_object_index(
                rettv->v_type = VAR_UNKNOWN;
 
                // Call the user function.  Result goes into rettv;
-               // TODO: pass the object
                int error = call_user_func_check(fp, argcount, argvars,
                                                        rettv, &funcexe, NULL);
 
@@ -545,11 +585,13 @@ class_unref(class_T *cl)
        }
        vim_free(cl->class_obj_members);
 
+       for (int i = 0; i < cl->class_obj_method_count; ++i)
+       {
+           ufunc_T *uf = cl->class_obj_methods[i];
+           func_clear_free(uf, FALSE);
+       }
        vim_free(cl->class_obj_methods);
 
-       if (cl->class_new_func != NULL)
-           func_ptr_unref(cl->class_new_func);
-
        clear_type_list(&cl->class_type_list);
 
        vim_free(cl);
index ab9e965690ec875e290b4911ae9e8d1ccbcb269b..08e273dd024600feeaf6ce2b32cfc0c28461b6ee 100644 (file)
@@ -52,7 +52,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
            CLEAR_POINTER(lvar);
            lvar->lv_name = (char_u *)"this";
            if (cctx->ctx_ufunc->uf_class != NULL)
-               lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_type;
+               lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_object_type;
        }
        return OK;
     }
@@ -975,7 +975,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
        goto theend;
     }
 
-    ufunc = define_function(eap, lambda_name, lines_to_free, NULL);
+    ufunc = define_function(eap, lambda_name, lines_to_free, FALSE);
     if (ufunc == NULL)
     {
        r = eap->skip ? OK : FAIL;
index 3b142b8586b19836ac88b31c502b1ba1c6eaec9e..96983bbdc35687e5637371e900f5e60bacada0a5 100644 (file)
@@ -4225,7 +4225,7 @@ exec_instructions(ectx_T *ectx)
                    CLEAR_FIELD(ea);
                    ea.cmd = ea.arg = iptr->isn_arg.string;
                    ga_init2(&lines_to_free, sizeof(char_u *), 50);
-                   define_function(&ea, NULL, &lines_to_free, NULL);
+                   define_function(&ea, NULL, &lines_to_free, FALSE);
                    ga_clear_strings(&lines_to_free);
                }
                break;
@@ -5114,6 +5114,35 @@ exec_instructions(ectx_T *ectx)
                }
                break;
 
+           case ISN_OBJ_MEMBER:
+               {
+                   tv = STACK_TV_BOT(-1);
+                   if (tv->v_type != VAR_OBJECT)
+                   {
+                       SOURCING_LNUM = iptr->isn_lnum;
+                       garray_T type_list;
+                       ga_init2(&type_list, sizeof(type_T *), 10);
+                       type_T *type = typval2type(tv, get_copyID(),
+                                                  &type_list, TVTT_DO_MEMBER);
+                       char *tofree = NULL;
+                       char *typename = type_name(type, &tofree);
+                       semsg(_(e_object_required_found_str), typename);
+                       vim_free(tofree);
+                       clear_type_list(&type_list);
+                       goto on_error;
+                   }
+                   int idx = iptr->isn_arg.number;
+                   object_T *obj = tv->vval.v_object;
+                   // the members are located right after the object struct
+                   typval_T *mtv = ((typval_T *)(obj + 1)) + idx;
+                   *tv = *mtv;
+
+                   // Unreference the object after getting the member, it may
+                   // be freed.
+                   object_unref(obj);
+               }
+               break;
+
            case ISN_CLEARDICT:
                dict_stack_drop();
                break;
@@ -5577,6 +5606,7 @@ call_def_function(
     typval_T   *argv,          // arguments
     int                flags,          // DEF_ flags
     partial_T  *partial,       // optional partial for context
+    object_T   *object,        // object, e.g. for this.Func()
     funccall_T *funccal,
     typval_T   *rettv)         // return value
 {
@@ -5818,6 +5848,15 @@ call_def_function(
            STACK_TV_VAR(idx)->vval.v_number = 0;
        }
        ectx.ec_stack.ga_len += dfunc->df_varcount;
+
+       if (object != NULL)
+       {
+           // the object is always the variable at index zero
+           tv = STACK_TV_VAR(0);
+           tv->v_type = VAR_OBJECT;
+           tv->vval.v_object = object;
+       }
+
        if (dfunc->df_has_closure)
        {
            // Initialize the variable that counts how many closures were
@@ -6766,6 +6805,8 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
            case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
            case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
                                                  iptr->isn_arg.string); break;
+           case ISN_OBJ_MEMBER: smsg("%s%4d OBJ_MEMBER %d", pfx, current,
+                                            (int)iptr->isn_arg.number); break;
            case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break;
            case ISN_USEDICT: smsg("%s%4d USEDICT", pfx, current); break;
 
index 0e2e8eac9423f14bbdfe0038757430057a3c4f3e..ca2d81ce640e914ce659177100c998336f044efc 100644 (file)
@@ -250,6 +250,56 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
     return OK;
 }
 
+/*
+ * Compile ".member" coming after an object or class.
+ */
+
+    static int
+compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
+{
+    if (VIM_ISWHITE((*arg)[1]))
+    {
+       semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
+       return FAIL;
+    }
+
+    ++*arg;
+    char_u *name = *arg;
+    char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
+    if (name_end == name)
+       return FAIL;
+    size_t len = name_end - name;
+
+    class_T *cl = (class_T *)type->tt_member;
+    if (*name_end == '(')
+    {
+       // TODO
+    }
+    else if (type->tt_type == VAR_OBJECT)
+    {
+       for (int i = 0; i < cl->class_obj_member_count; ++i)
+       {
+           objmember_T *m = &cl->class_obj_members[i];
+           if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
+           {
+               generate_OBJ_MEMBER(cctx, i, m->om_type);
+
+               *arg = name_end;
+               return OK;
+           }
+       }
+
+       semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
+    }
+    else
+    {
+       // TODO: class member
+       emsg("compile_class_object_index(): not handled");
+    }
+
+    return FAIL;
+}
+
 /*
  * Generate an instruction to load script-local variable "name", without the
  * leading "s:".
@@ -1797,6 +1847,7 @@ compile_subscript(
     for (;;)
     {
        char_u *p = skipwhite(*arg);
+       type_T *type;
 
        if (*p == NUL || (VIM_ISWHITE(**arg) && vim9_comment_start(p)))
        {
@@ -1824,7 +1875,6 @@ compile_subscript(
        // is not a function call.
        if (**arg == '(')
        {
-           type_T      *type;
            int         argcount = 0;
 
            if (generate_ppconst(cctx, ppconst) == FAIL)
@@ -1911,7 +1961,6 @@ compile_subscript(
                int         argcount = 1;
                garray_T    *stack = &cctx->ctx_type_stack;
                int         type_idx_start = stack->ga_len;
-               type_T      *type;
                int         expr_isn_start = cctx->ctx_instr.ga_len;
                int         expr_isn_end;
                int         arg_isn_count;
@@ -2097,6 +2146,18 @@ compile_subscript(
            if (compile_member(is_slice, &keeping_dict, cctx) == FAIL)
                return FAIL;
        }
+       else if (*p == '.'
+               && (type = get_type_on_stack(cctx, 0)) != &t_unknown
+               && (type->tt_type == VAR_CLASS || type->tt_type == VAR_OBJECT))
+       {
+           // class member: SomeClass.varname
+           // class method: SomeClass.SomeMethod()
+           // class constructor: SomeClass.new()
+           // object member: someObject.varname, this.varname
+           // object method: someObject.SomeMethod(), this.SomeMethod()
+           if (compile_class_object_index(cctx, arg, type) == FAIL)
+               return FAIL;
+       }
        else if (*p == '.' && p[1] != '.')
        {
            // dictionary member: dict.name
index cd5597db6417061bc29ad9f3b1b60729ff70d05a..88bd2ca42a3e36a0400249ed5b8d510e3e00d2fb 100644 (file)
@@ -131,6 +131,23 @@ generate_CONSTRUCT(cctx_T *cctx, class_T *cl)
     return OK;
 }
 
+/*
+ * Generate ISN_OBJ_MEMBER - access object member by indes.
+ */
+    int
+generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
+{
+    RETURN_OK_IF_SKIP(cctx);
+
+    // drop the object type
+    isn_T *isn = generate_instr_drop(cctx, ISN_OBJ_MEMBER, 1);
+    if (isn == NULL)
+       return FAIL;
+
+    isn->isn_arg.number = idx;
+    return push_type_stack2(cctx, type, &t_any);
+}
+
 /*
  * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
  * But only for simple types.
@@ -2460,6 +2477,7 @@ delete_instr(isn_T *isn)
        case ISN_NEWDICT:
        case ISN_NEWLIST:
        case ISN_NEWPARTIAL:
+       case ISN_OBJ_MEMBER:
        case ISN_OPANY:
        case ISN_OPFLOAT:
        case ISN_OPNR:
index b25541122228458fa23723094cd903381a5529ba..f36fa5eea4500e5dbe51e8f89969dcb7f895396d 100644 (file)
@@ -1585,10 +1585,7 @@ f_typename(typval_T *argvars, typval_T *rettv)
     if (tofree != NULL)
        rettv->vval.v_string = (char_u *)tofree;
     else
-    {
        rettv->vval.v_string = vim_strsave((char_u *)name);
-       vim_free(tofree);
-    }
     clear_type_list(&type_list);
 }