To: vim_dev@googlegroups.com Subject: Patch 9.0.1209 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1209 Problem: Getting interface member does not always work. Solution: Convert the index on the interface to the index on the object. (closes #11825) Files: src/vim9instr.c, src/proto/vim9instr.pro, src/vim9.h, src/structs.h, src/vim9class.c, src/proto/vim9class.pro, src/vim9compile.c, src/vim9expr.c, src/vim9execute.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1208/src/vim9instr.c 2023-01-15 18:17:08.785655216 +0000 --- src/vim9instr.c 2023-01-16 19:30:37.663190725 +0000 *************** *** 150,155 **** --- 150,175 ---- } /* + * Generate ISN_GET_ITF_MEMBER - access member of interface at bottom of stack + * by index. + */ + int + generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type) + { + RETURN_OK_IF_SKIP(cctx); + + // drop the object type + isn_T *isn = generate_instr_drop(cctx, ISN_GET_ITF_MEMBER, 1); + if (isn == NULL) + return FAIL; + + isn->isn_arg.classmember.cm_class = itf; + ++itf->class_refcount; + isn->isn_arg.classmember.cm_idx = idx; + return push_type_stack2(cctx, type, &t_any); + } + + /* * Generate ISN_STORE_THIS - store value in member of "this" object with member * index "idx". */ *************** *** 2497,2502 **** --- 2517,2523 ---- case ISN_LOAD_CLASSMEMBER: case ISN_STORE_CLASSMEMBER: + case ISN_GET_ITF_MEMBER: class_unref(isn->isn_arg.classmember.cm_class); break; *** ../vim-9.0.1208/src/proto/vim9instr.pro 2022-12-29 20:56:20.021538298 +0000 --- src/proto/vim9instr.pro 2023-01-16 19:24:04.803590627 +0000 *************** *** 5,10 **** --- 5,11 ---- isn_T *generate_instr_debug(cctx_T *cctx); int generate_CONSTRUCT(cctx_T *cctx, class_T *cl); int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type); + int generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int itf_idx, type_T *type); int generate_STORE_THIS(cctx_T *cctx, int idx); 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); *** ../vim-9.0.1208/src/vim9.h 2023-01-02 20:32:18.425749782 +0000 --- src/vim9.h 2023-01-16 18:39:02.236202909 +0000 *************** *** 34,43 **** ISN_INSTR, // instructions compiled from expression ISN_CONSTRUCT, // construct an object, using contstruct_T ISN_GET_OBJ_MEMBER, // object member, index is isn_arg.number ISN_STORE_THIS, // store value in "this" object member, index is // isn_arg.number ! ISN_LOAD_CLASSMEMBER, // load class member, using classmember_T ! ISN_STORE_CLASSMEMBER, // store in class member, using classmember_T // get and set variables ISN_LOAD, // push local variable isn_arg.number --- 34,44 ---- ISN_INSTR, // instructions compiled from expression ISN_CONSTRUCT, // construct an object, using contstruct_T ISN_GET_OBJ_MEMBER, // object member, index is isn_arg.number + ISN_GET_ITF_MEMBER, // interface member, index is isn_arg.classmember ISN_STORE_THIS, // store value in "this" object member, index is // isn_arg.number ! ISN_LOAD_CLASSMEMBER, // load class member, using isn_arg.classmember ! ISN_STORE_CLASSMEMBER, // store in class member, using isn_arg.classmember // get and set variables ISN_LOAD, // push local variable isn_arg.number *************** *** 480,486 **** class_T *construct_class; // class the object is created from } construct_T; ! // arguments to ISN_STORE_CLASSMEMBER and ISN_LOAD_CLASSMEMBER typedef struct { class_T *cm_class; int cm_idx; --- 481,487 ---- class_T *construct_class; // class the object is created from } construct_T; ! // arguments to ISN_STORE_CLASSMEMBER, ISN_LOAD_CLASSMEMBER, ISN_GET_ITF_MEMBER typedef struct { class_T *cm_class; int cm_idx; *** ../vim-9.0.1208/src/structs.h 2023-01-12 15:01:28.833314453 +0000 --- src/structs.h 2023-01-16 19:01:20.196256781 +0000 *************** *** 1483,1488 **** --- 1483,1496 ---- char_u *ocm_init; // allocated } ocmember_T; + // used for the lookup table of a class member index + typedef struct itf2class_S itf2class_T; + struct itf2class_S { + itf2class_T *i2c_next; + class_T *i2c_class; + // array with ints follows + }; + #define CLASS_INTERFACE 1 // "class_T": used for v_class of typval of VAR_CLASS *************** *** 1501,1506 **** --- 1509,1515 ---- int class_interface_count; char_u **class_interfaces; // allocated array of names class_T **class_interfaces_cl; // interfaces (counts as reference) + itf2class_T *class_itf2class; // member index lookup tables // class members: "static varname" int class_class_member_count; *** ../vim-9.0.1208/src/vim9class.c 2023-01-15 20:48:56.751583003 +0000 --- src/vim9class.c 2023-01-16 19:40:09.482927790 +0000 *************** *** 197,202 **** --- 197,228 ---- } /* + * Convert a member index "idx" of interface "itf" to the member index of class + * "cl" implementing that interface. + */ + int + object_index_from_itf_index(class_T *itf, int idx, class_T *cl) + { + if (idx > itf->class_obj_member_count) + { + siemsg("index %d out of range for interface %s", idx, itf->class_name); + return 0; + } + itf2class_T *i2c; + for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next) + if (i2c->i2c_class == cl) + break; + if (i2c == NULL) + { + siemsg("class %s not found on interface %s", + cl->class_name, itf->class_name); + return 0; + } + int *table = (int *)(i2c + 1); + return table[idx]; + } + + /* * Handle ":class" and ":abstract class" up to ":endclass". * Handle ":interface" up to ":endinterface". */ *************** *** 765,786 **** cl->class_extends = extends_cl; - if (ga_impl.ga_len > 0) - { - // Move the "implements" names into the class. - cl->class_interface_count = ga_impl.ga_len; - cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len); - if (cl->class_interfaces == NULL) - goto cleanup; - for (int i = 0; i < ga_impl.ga_len; ++i) - cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i]; - VIM_CLEAR(ga_impl.ga_data); - ga_impl.ga_len = 0; - - cl->class_interfaces_cl = intf_classes; - intf_classes = NULL; - } - // Add class and object members to "cl". if (add_members_to_class(&classmembers, extends_cl == NULL ? NULL --- 791,796 ---- *************** *** 798,803 **** --- 808,855 ---- &cl->class_obj_member_count) == FAIL) goto cleanup; + if (ga_impl.ga_len > 0) + { + // Move the "implements" names into the class. + cl->class_interface_count = ga_impl.ga_len; + cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len); + if (cl->class_interfaces == NULL) + goto cleanup; + for (int i = 0; i < ga_impl.ga_len; ++i) + cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i]; + VIM_CLEAR(ga_impl.ga_data); + ga_impl.ga_len = 0; + + // For each interface add a lookuptable for the member index on the + // interface to the member index in this class. + for (int i = 0; i < cl->class_interface_count; ++i) + { + class_T *ifcl = intf_classes[i]; + itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T) + + ifcl->class_obj_member_count * sizeof(int)); + if (if2cl == NULL) + goto cleanup; + if2cl->i2c_next = ifcl->class_itf2class; + ifcl->class_itf2class = if2cl; + if2cl->i2c_class = cl; + + for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i) + for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i) + { + if (STRCMP(ifcl->class_obj_members[if_i].ocm_name, + cl->class_obj_members[cl_i].ocm_name) == 0) + { + int *table = (int *)(if2cl + 1); + table[if_i] = cl_i; + break; + } + } + } + + cl->class_interfaces_cl = intf_classes; + intf_classes = NULL; + } + if (is_class && cl->class_class_member_count > 0) { // Allocate a typval for each class member and initialize it. *************** *** 1411,1416 **** --- 1463,1475 ---- vim_free(cl->class_interfaces); vim_free(cl->class_interfaces_cl); + itf2class_T *next; + for (itf2class_T *i2c = cl->class_itf2class; i2c != NULL; i2c = next) + { + next = i2c->i2c_next; + vim_free(i2c); + } + for (int i = 0; i < cl->class_class_member_count; ++i) { ocmember_T *m = &cl->class_class_members[i]; *** ../vim-9.0.1208/src/proto/vim9class.pro 2023-01-05 19:59:14.003418087 +0000 --- src/proto/vim9class.pro 2023-01-16 19:23:57.059601710 +0000 *************** *** 1,4 **** --- 1,5 ---- /* vim9class.c */ + int object_index_from_itf_index(class_T *itf, int idx, class_T *cl); void ex_class(exarg_T *eap); type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int *member_idx); void ex_enum(exarg_T *eap); *** ../vim-9.0.1208/src/vim9compile.c 2023-01-15 15:51:44.078750474 +0000 --- src/vim9compile.c 2023-01-16 19:28:56.955268310 +0000 *************** *** 2056,2061 **** --- 2056,2063 ---- if (generate_LOAD(cctx, ISN_LOAD, 0, NULL, lhs->lhs_type) == FAIL) return FAIL; + if (cl->class_flags & CLASS_INTERFACE) + return generate_GET_ITF_MEMBER(cctx, cl, lhs->lhs_member_idx, type); return generate_GET_OBJ_MEMBER(cctx, lhs->lhs_member_idx, type); } *** ../vim-9.0.1208/src/vim9expr.c 2023-01-15 20:18:51.472362682 +0000 --- src/vim9expr.c 2023-01-16 19:29:48.503226915 +0000 *************** *** 364,369 **** --- 364,371 ---- } *arg = name_end; + if (cl->class_flags & CLASS_INTERFACE) + return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type); return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type); } } *** ../vim-9.0.1208/src/vim9execute.c 2023-01-05 19:59:14.007418126 +0000 --- src/vim9execute.c 2023-01-16 19:29:39.443233917 +0000 *************** *** 5184,5189 **** --- 5184,5190 ---- break; case ISN_GET_OBJ_MEMBER: + case ISN_GET_ITF_MEMBER: { tv = STACK_TV_BOT(-1); if (tv->v_type != VAR_OBJECT) *************** *** 5200,5207 **** 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; copy_tv(mtv, tv); --- 5201,5220 ---- clear_type_list(&type_list); goto on_error; } ! object_T *obj = tv->vval.v_object; + int idx; + if (iptr->isn_type == ISN_GET_OBJ_MEMBER) + idx = iptr->isn_arg.number; + else + { + idx = iptr->isn_arg.classmember.cm_idx; + // convert the interface index to the object index + idx = object_index_from_itf_index( + iptr->isn_arg.classmember.cm_class, + idx, obj->obj_class); + } + // the members are located right after the object struct typval_T *mtv = ((typval_T *)(obj + 1)) + idx; copy_tv(mtv, tv); *************** *** 6912,6917 **** --- 6925,6935 ---- iptr->isn_arg.string); break; case ISN_GET_OBJ_MEMBER: smsg("%s%4d OBJ_MEMBER %d", pfx, current, (int)iptr->isn_arg.number); break; + case ISN_GET_ITF_MEMBER: smsg("%s%4d ITF_MEMBER %d on %s", + pfx, current, + (int)iptr->isn_arg.classmember.cm_idx, + iptr->isn_arg.classmember.cm_class->class_name); + break; case ISN_STORE_THIS: smsg("%s%4d STORE_THIS %d", pfx, current, (int)iptr->isn_arg.number); break; case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break; *** ../vim-9.0.1208/src/testdir/test_vim9_class.vim 2023-01-16 16:39:33.495018026 +0000 --- src/testdir/test_vim9_class.vim 2023-01-16 19:36:03.551012970 +0000 *************** *** 870,875 **** --- 870,901 ---- endclass END v9.CheckScriptFailure(lines, 'E1349: Function "Methods" of interface "Some" not implemented') + + # Check different order of members in class and interface works. + lines =<< trim END + vim9script + + interface Result + this.label: string + this.errpos: number + endinterface + + # order of members is opposite of interface + class Failure implements Result + this.errpos: number = 42 + this.label: string = 'label' + endclass + + def Test() + var result: Result = Failure.new() + + assert_equal('label', result.label) + assert_equal(42, result.errpos) + enddef + + Test() + END + v9.CheckScriptSuccess(lines) enddef def Test_class_used_as_type() *** ../vim-9.0.1208/src/version.c 2023-01-16 18:19:01.911301085 +0000 --- src/version.c 2023-01-16 19:33:59.827069580 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1209, /**/ -- Far out in the uncharted backwaters of the unfashionable end of the Western Spiral arm of the Galaxy lies a small unregarded yellow sun. Orbiting this at a distance of roughly ninety-eight million miles is an utterly insignificant little blue-green planet whose ape-descended life forms are so amazingly primitive that they still think digital watches are a pretty neat idea ... -- Douglas Adams, "The Hitchhiker's Guide to the Galaxy" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///