]> granicus.if.org Git - vim/commitdiff
patch 9.0.1357: using null_object results in an internal error v9.0.1357
authorBram Moolenaar <Bram@vim.org>
Sun, 26 Feb 2023 18:58:23 +0000 (18:58 +0000)
committerBram Moolenaar <Bram@vim.org>
Sun, 26 Feb 2023 18:58:23 +0000 (18:58 +0000)
Problem:    Using null_object results in an internal error. (Ernie Rael)
Solution:   Add instructions for pushing an object and class. (closes #12044)

src/errors.h
src/testdir/test_vim9_class.vim
src/version.c
src/vim9.h
src/vim9execute.c
src/vim9instr.c

index a5df8edcd71f079281dda028024657a2fc4c108f..f1c36ad7bb9c4c59ca8ae5ee3f2babd3765f38bf 100644 (file)
@@ -3449,3 +3449,7 @@ EXTERN char e_using_null_object[]
 #endif
 EXTERN char e_cannot_use_color_none_did_you_mean_none[]
        INIT(= N_("E1361: Cannot use color \"none\", did you mean \"NONE\"?"));
+#ifdef FEAT_EVAL
+EXTERN char e_cannot_use_non_null_object[]
+       INIT(= N_("E1362: Cannot use a non-null object"));
+#endif
index 87c1d4a08c0a5f8d4f1c7addebc1d9ee54701387..3bb289b47e26da02aab0d3556bda33f4d2641445 100644 (file)
@@ -186,6 +186,23 @@ def Test_class_defined_twice()
   source XclassTwice.vim
 enddef
 
+def Test_returning_null_object()
+  # this was causing an internal error
+  var lines =<< trim END
+      vim9script
+
+      class BufferList
+          def Current(): any
+              return null_object
+          enddef
+      endclass
+
+      var buffers = BufferList.new()
+      echo buffers.Current()
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 def Test_class_interface_wrong_end()
   var lines =<< trim END
       vim9script
index ffe4a8553844eed4b25bb564c73a97067a231281..a85b81cc8462cb72cf38ca081c42761395db5dfa 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1357,
 /**/
     1356,
 /**/
index 4df97c6447b309950b71b83f3492686b75d87a23..d9b25dae5b478624dc4529a6d69865feac434db1 100644 (file)
@@ -101,6 +101,8 @@ typedef enum {
     ISN_PUSHFUNC,      // push func isn_arg.string
     ISN_PUSHCHANNEL,   // push NULL channel
     ISN_PUSHJOB,       // push NULL job
+    ISN_PUSHOBJ,       // push NULL object
+    ISN_PUSHCLASS,     // push class, uses isn_arg.class
     ISN_NEWLIST,       // push list from stack items, size is isn_arg.number
                        // -1 for null_list
     ISN_NEWDICT,       // push dict from stack items, size is isn_arg.number
@@ -518,6 +520,7 @@ struct isn_S {
        channel_T           *channel;
        job_T               *job;
        partial_T           *partial;
+       class_T             *class;
        jump_T              jump;
        jumparg_T           jumparg;
        forloop_T           forloop;
index 638dbc5ca40c4405d89503576faf4dbad5bee20e..90594ef92507494746ea05877034fa1ab6883920 100644 (file)
@@ -1944,8 +1944,7 @@ exec_command(isn_T *iptr)
     source_cookie_T cookie;
 
     SOURCING_LNUM = iptr->isn_lnum;
-    // Pass getsourceline to get an error for a missing ":end"
-    // command.
+    // Pass getsourceline to get an error for a missing ":end" command.
     CLEAR_FIELD(cookie);
     cookie.sourcing_lnum = iptr->isn_lnum - 1;
     if (do_cmdline(iptr->isn_arg.string,
@@ -4018,6 +4017,8 @@ exec_instructions(ectx_T *ectx)
            case ISN_PUSHFUNC:
            case ISN_PUSHCHANNEL:
            case ISN_PUSHJOB:
+           case ISN_PUSHOBJ:
+           case ISN_PUSHCLASS:
                if (GA_GROW_FAILS(&ectx->ec_stack, 1))
                    goto theend;
                tv = STACK_TV_BOT(0);
@@ -4064,6 +4065,14 @@ exec_instructions(ectx_T *ectx)
                        tv->vval.v_job = NULL;
 #endif
                        break;
+                   case ISN_PUSHOBJ:
+                       tv->v_type = VAR_OBJECT;
+                       tv->vval.v_object = NULL;
+                       break;
+                   case ISN_PUSHCLASS:
+                       tv->v_type = VAR_CLASS;
+                       tv->vval.v_class = iptr->isn_arg.class;
+                       break;
                    default:
                        tv->v_type = VAR_STRING;
                        tv->vval.v_string = iptr->isn_arg.string == NULL
@@ -6662,6 +6671,14 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                smsg("%s%4d PUSHJOB \"no process\"", pfx, current);
 #endif
                break;
+           case ISN_PUSHOBJ:
+               smsg("%s%4d PUSHOBJ null", pfx, current);
+               break;
+           case ISN_PUSHCLASS:
+               smsg("%s%4d PUSHCLASS %s", pfx, current,
+                       iptr->isn_arg.class == NULL ? "null"
+                                   : (char *)iptr->isn_arg.class->class_name);
+               break;
            case ISN_PUSHEXC:
                smsg("%s%4d PUSH v:exception", pfx, current);
                break;
index cdf037942c71a599796ba1cd8c8df919ac5b4ca2..4f228e4aa712ccf54019a5e5bc278c3d153b9d07 100644 (file)
@@ -655,6 +655,35 @@ generate_SETTYPE(
     return OK;
 }
 
+/*
+ * Generate an ISN_PUSHOBJ instruction.  Object is always NULL.
+ */
+    static int
+generate_PUSHOBJ(cctx_T *cctx)
+{
+    RETURN_OK_IF_SKIP(cctx);
+    if (generate_instr_type(cctx, ISN_PUSHOBJ, &t_any) == NULL)
+       return FAIL;
+    return OK;
+}
+
+/*
+ * Generate an ISN_PUSHCLASS instruction.  "class" can be NULL.
+ */
+    static int
+generate_PUSHCLASS(cctx_T *cctx, class_T *class)
+{
+    RETURN_OK_IF_SKIP(cctx);
+    isn_T *isn = generate_instr_type(cctx, ISN_PUSHCLASS,
+                                 class == NULL ? &t_any : &class->class_type);
+    if (isn == NULL)
+       return FAIL;
+    isn->isn_arg.class = class;
+    if (class != NULL)
+       ++class->class_refcount;
+    return OK;
+}
+
 /*
  * Generate a PUSH instruction for "tv".
  * "tv" will be consumed or cleared.
@@ -718,6 +747,17 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
            generate_PUSHS(cctx, &tv->vval.v_string);
            tv->vval.v_string = NULL;
            break;
+       case VAR_OBJECT:
+           if (tv->vval.v_object != NULL)
+           {
+               emsg(_(e_cannot_use_non_null_object));
+               return FAIL;
+           }
+           generate_PUSHOBJ(cctx);
+           break;
+       case VAR_CLASS:
+           generate_PUSHCLASS(cctx, tv->vval.v_class);
+           break;
        default:
            siemsg("constant type %d not supported", tv->v_type);
            clear_tv(tv);
@@ -1937,7 +1977,8 @@ generate_PCALL(
        ret_type = &t_any;
     else if (type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL)
     {
-       if (check_func_args_from_type(cctx, type, argcount, at_top, name) == FAIL)
+       if (check_func_args_from_type(cctx, type, argcount, at_top, name)
+                                                                      == FAIL)
            return FAIL;
 
        ret_type = type->tt_member;
@@ -2467,6 +2508,10 @@ delete_instr(isn_T *isn)
            blob_unref(isn->isn_arg.blob);
            break;
 
+       case ISN_PUSHCLASS:
+           class_unref(isn->isn_arg.class);
+           break;
+
        case ISN_UCALL:
            vim_free(isn->isn_arg.ufunc.cuf_name);
            break;
@@ -2659,6 +2704,7 @@ delete_instr(isn_T *isn)
        case ISN_PUSHF:
        case ISN_PUSHJOB:
        case ISN_PUSHNR:
+       case ISN_PUSHOBJ:
        case ISN_PUSHSPEC:
        case ISN_PUT:
        case ISN_REDIREND: