comparison src/vim9cmds.c @ 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 6340c608ca54
children 4a62e78803db
comparison
equal deleted inserted replaced
33392:3d3d0492824e 33393:016d8f863230
187 { 187 {
188 cctx_T *cctx = coookie; 188 cctx_T *cctx = coookie;
189 int cc = *name_end; 189 int cc = *name_end;
190 char_u *p = lvp->ll_name; 190 char_u *p = lvp->ll_name;
191 int ret = OK; 191 int ret = OK;
192 size_t len;
193 char_u *buf; 192 char_u *buf;
194 isntype_T isn = ISN_EXEC; 193 isntype_T isn = ISN_EXEC;
195 char *cmd = eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar"; 194 char *cmd = eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar";
195 int is_arg = FALSE;
196
197 #ifdef LOG_LOCKVAR
198 ch_log(NULL, "LKVAR: compile_lock_unlock(): cookie %p, name %s",
199 coookie, p);
200 #endif
196 201
197 if (cctx->ctx_skip == SKIP_YES) 202 if (cctx->ctx_skip == SKIP_YES)
198 return OK; 203 return OK;
199 204
200 if (*p == NUL) 205 if (*p == NUL)
205 210
206 // Cannot use :lockvar and :unlockvar on local variables. 211 // Cannot use :lockvar and :unlockvar on local variables.
207 if (p[1] != ':') 212 if (p[1] != ':')
208 { 213 {
209 char_u *end = find_name_end(p, NULL, NULL, FNE_CHECK_START); 214 char_u *end = find_name_end(p, NULL, NULL, FNE_CHECK_START);
210 215 // If name is is locally accessible, except for local var,
211 if (lookup_local(p, end - p, NULL, cctx) == OK) 216 // then put it on the stack to use with ISN_LOCKUNLOCK.
212 { 217 // This could be v.memb, v[idx_key]; bare class variable,
213 char_u *s = p; 218 // function arg. The local variable on the stack, will be passed
214 219 // to ex_lockvar() indirectly.
215 if (*end != '.' && *end != '[') 220
221 char_u *name = NULL;
222 int len = end - p;
223
224 if (lookup_local(p, len, NULL, cctx) == OK)
225 {
226 // Handle "this", "this.val", "anyvar[idx]"
227 if (*end != '.' && *end != '['
228 && (len != 4 || STRNCMP("this", p, len) != 0))
216 { 229 {
217 emsg(_(e_cannot_lock_unlock_local_variable)); 230 emsg(_(e_cannot_lock_unlock_local_variable));
218 return FAIL; 231 return FAIL;
219 } 232 }
220 233 // Push the local on the stack, could be "this".
221 // For "d.member" put the local variable on the stack, it will be 234 name = p;
222 // passed to ex_lockvar() indirectly. 235 #ifdef LOG_LOCKVAR
223 if (compile_load(&s, end, cctx, FALSE, FALSE) == FAIL) 236 ch_log(NULL, "LKVAR: compile... lookup_local: name %s", name);
237 #endif
238 }
239 if (name == NULL)
240 {
241 class_T *cl;
242 if (cctx_class_member_idx(cctx, p, len, &cl) >= 0)
243 {
244 if (*end != '.' && *end != '[')
245 {
246 // Push the class of the bare class variable name
247 name = cl->class_name;
248 len = STRLEN(name);
249 #ifdef LOG_LOCKVAR
250 ch_log(NULL, "LKVAR: compile... cctx_class_member: name %s",
251 name);
252 #endif
253 }
254 }
255 }
256 if (name == NULL)
257 {
258 int idx;
259 type_T *type;
260 // Can lockvar any function arg.
261 // TODO: test arg[idx]/arg.member
262 if (arg_exists(p, len, &idx, &type, NULL, cctx) == OK)
263 {
264 name = p;
265 is_arg = TRUE;
266 #ifdef LOG_LOCKVAR
267 ch_log(NULL, "LKVAR: compile... arg_exists: name %s", name);
268 #endif
269 }
270 }
271 if (name != NULL)
272 {
273 #ifdef LOG_LOCKVAR
274 ch_log(NULL, "LKVAR: compile... INS_LOCKUNLOCK %s", name);
275 #endif
276 if (compile_load(&name, name + len, cctx, FALSE, FALSE) == FAIL)
224 return FAIL; 277 return FAIL;
225 isn = ISN_LOCKUNLOCK; 278 isn = ISN_LOCKUNLOCK;
226 } 279 }
227 } 280 }
228 281
229 // Checking is done at runtime. 282 // Checking is done at runtime.
230 *name_end = NUL; 283 *name_end = NUL;
231 len = name_end - p + 20; 284 size_t len = name_end - p + 20;
232 buf = alloc(len); 285 buf = alloc(len);
233 if (buf == NULL) 286 if (buf == NULL)
234 ret = FAIL; 287 ret = FAIL;
235 else 288 else
236 { 289 {
237 if (deep < 0) 290 if (deep < 0)
238 vim_snprintf((char *)buf, len, "%s! %s", cmd, p); 291 vim_snprintf((char *)buf, len, "%s! %s", cmd, p);
239 else 292 else
240 vim_snprintf((char *)buf, len, "%s %d %s", cmd, deep, p); 293 vim_snprintf((char *)buf, len, "%s %d %s", cmd, deep, p);
241 ret = generate_EXEC_copy(cctx, isn, buf); 294 #ifdef LOG_LOCKVAR
295 ch_log(NULL, "LKVAR: compile... buf %s", buf);
296 #endif
297 if (isn == ISN_LOCKUNLOCK)
298 ret = generate_LOCKUNLOCK(cctx, buf, is_arg);
299 else
300 ret = generate_EXEC_copy(cctx, isn, buf);
242 301
243 vim_free(buf); 302 vim_free(buf);
244 *name_end = cc; 303 *name_end = cc;
245 } 304 }
246 return ret; 305 return ret;