| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header: d:/cvsroot/tads/tads3/VMLST.CPP,v 1.3 1999/05/17 02:52:28 MJRoberts Exp $"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | vmlst.cpp - list metaclass |
| | 15 | Function |
| | 16 | |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 10/29/98 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | #include <stdlib.h> |
| | 24 | #include <string.h> |
| | 25 | |
| | 26 | #include "t3std.h" |
| | 27 | #include "vmmcreg.h" |
| | 28 | #include "vmtype.h" |
| | 29 | #include "vmlst.h" |
| | 30 | #include "vmobj.h" |
| | 31 | #include "vmerr.h" |
| | 32 | #include "vmerrnum.h" |
| | 33 | #include "vmfile.h" |
| | 34 | #include "vmpool.h" |
| | 35 | #include "vmstack.h" |
| | 36 | #include "vmmeta.h" |
| | 37 | #include "vmrun.h" |
| | 38 | #include "vmbif.h" |
| | 39 | #include "vmpredef.h" |
| | 40 | #include "vmiter.h" |
| | 41 | #include "vmsort.h" |
| | 42 | |
| | 43 | |
| | 44 | /* ------------------------------------------------------------------------ */ |
| | 45 | /* |
| | 46 | * statics |
| | 47 | */ |
| | 48 | |
| | 49 | /* metaclass registration object */ |
| | 50 | static CVmMetaclassList metaclass_reg_obj; |
| | 51 | CVmMetaclass *CVmObjList::metaclass_reg_ = &metaclass_reg_obj; |
| | 52 | |
| | 53 | /* function table */ |
| | 54 | int (*CVmObjList::func_table_[])(VMG_ vm_val_t *retval, |
| | 55 | const vm_val_t *self_val, |
| | 56 | const char *lst, uint *argc) = |
| | 57 | { |
| | 58 | &CVmObjList::getp_undef, |
| | 59 | &CVmObjList::getp_subset, |
| | 60 | &CVmObjList::getp_map, |
| | 61 | &CVmObjList::getp_len, |
| | 62 | &CVmObjList::getp_sublist, |
| | 63 | &CVmObjList::getp_intersect, |
| | 64 | &CVmObjList::getp_index_of, |
| | 65 | &CVmObjList::getp_car, |
| | 66 | &CVmObjList::getp_cdr, |
| | 67 | &CVmObjList::getp_index_which, |
| | 68 | &CVmObjList::getp_for_each, |
| | 69 | &CVmObjList::getp_val_which, |
| | 70 | &CVmObjList::getp_last_index_of, |
| | 71 | &CVmObjList::getp_last_index_which, |
| | 72 | &CVmObjList::getp_last_val_which, |
| | 73 | &CVmObjList::getp_count_of, |
| | 74 | &CVmObjList::getp_count_which, |
| | 75 | &CVmObjList::getp_get_unique, |
| | 76 | &CVmObjList::getp_append_unique, |
| | 77 | &CVmObjList::getp_append, |
| | 78 | &CVmObjList::getp_sort, |
| | 79 | &CVmObjList::getp_prepend, |
| | 80 | &CVmObjList::getp_insert_at, |
| | 81 | &CVmObjList::getp_remove_element_at, |
| | 82 | &CVmObjList::getp_remove_range, |
| | 83 | &CVmObjList::getp_for_each_assoc |
| | 84 | }; |
| | 85 | |
| | 86 | |
| | 87 | /* ------------------------------------------------------------------------ */ |
| | 88 | /* |
| | 89 | * Static creation methods. These routines allocate an object ID and |
| | 90 | * create a new list object. |
| | 91 | */ |
| | 92 | |
| | 93 | /* create dynamically using stack arguments */ |
| | 94 | vm_obj_id_t CVmObjList::create_from_stack(VMG_ const uchar **pc_ptr, |
| | 95 | uint argc) |
| | 96 | { |
| | 97 | vm_obj_id_t id; |
| | 98 | CVmObjList *lst; |
| | 99 | size_t idx; |
| | 100 | |
| | 101 | /* |
| | 102 | * create the list - this type of construction is never used for |
| | 103 | * root set objects |
| | 104 | */ |
| | 105 | id = vm_new_id(vmg_ FALSE, TRUE, FALSE); |
| | 106 | |
| | 107 | /* create a list with one element per argument */ |
| | 108 | lst = new (vmg_ id) CVmObjList(vmg_ argc); |
| | 109 | |
| | 110 | /* add each argument */ |
| | 111 | for (idx = 0 ; idx < argc ; ++idx) |
| | 112 | { |
| | 113 | /* retrieve the next element from the stack and add it to the list */ |
| | 114 | lst->cons_set_element(idx, G_stk->get(idx)); |
| | 115 | } |
| | 116 | |
| | 117 | /* discard the stack parameters */ |
| | 118 | G_stk->discard(argc); |
| | 119 | |
| | 120 | /* return the new object */ |
| | 121 | return id; |
| | 122 | } |
| | 123 | |
| | 124 | /* |
| | 125 | * create dynamically from the current method's parameters |
| | 126 | */ |
| | 127 | vm_obj_id_t CVmObjList::create_from_params(VMG_ uint param_idx, uint cnt) |
| | 128 | { |
| | 129 | vm_obj_id_t id; |
| | 130 | CVmObjList *lst; |
| | 131 | size_t idx; |
| | 132 | |
| | 133 | /* create the new list object as a non-root-set object */ |
| | 134 | id = vm_new_id(vmg_ FALSE, TRUE, FALSE); |
| | 135 | lst = new (vmg_ id) CVmObjList(vmg_ cnt); |
| | 136 | |
| | 137 | /* copy each parameter into the new list */ |
| | 138 | for (idx = 0 ; cnt != 0 ; --cnt, ++param_idx, ++idx) |
| | 139 | { |
| | 140 | /* retrieve the next element and add it to the list */ |
| | 141 | lst->cons_set_element(idx, |
| | 142 | G_interpreter->get_param(vmg_ param_idx)); |
| | 143 | } |
| | 144 | |
| | 145 | /* return the new object */ |
| | 146 | return id; |
| | 147 | } |
| | 148 | |
| | 149 | /* create a list with no initial contents */ |
| | 150 | vm_obj_id_t CVmObjList::create(VMG_ int in_root_set) |
| | 151 | { |
| | 152 | vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); |
| | 153 | new (vmg_ id) CVmObjList(); |
| | 154 | return id; |
| | 155 | } |
| | 156 | |
| | 157 | /* create a list with a given number of elements */ |
| | 158 | vm_obj_id_t CVmObjList::create(VMG_ int in_root_set, |
| | 159 | size_t element_count) |
| | 160 | { |
| | 161 | vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); |
| | 162 | new (vmg_ id) CVmObjList(vmg_ element_count); |
| | 163 | return id; |
| | 164 | } |
| | 165 | |
| | 166 | /* create a list by copying a constant list */ |
| | 167 | vm_obj_id_t CVmObjList::create(VMG_ int in_root_set, const char *lst) |
| | 168 | { |
| | 169 | vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); |
| | 170 | new (vmg_ id) CVmObjList(vmg_ lst); |
| | 171 | return id; |
| | 172 | } |
| | 173 | |
| | 174 | /* ------------------------------------------------------------------------ */ |
| | 175 | /* |
| | 176 | * Constructors. These are called indirectly through our static |
| | 177 | * creation methods. |
| | 178 | */ |
| | 179 | |
| | 180 | /* |
| | 181 | * create a list object from a constant list |
| | 182 | */ |
| | 183 | CVmObjList::CVmObjList(VMG_ const char *lst) |
| | 184 | { |
| | 185 | size_t cnt; |
| | 186 | |
| | 187 | /* get the element count from the original list */ |
| | 188 | cnt = vmb_get_len(lst); |
| | 189 | |
| | 190 | /* allocate space */ |
| | 191 | alloc_list(vmg_ cnt); |
| | 192 | |
| | 193 | /* copy the list's contents */ |
| | 194 | memcpy(ext_, lst, calc_alloc(cnt)); |
| | 195 | } |
| | 196 | |
| | 197 | /* |
| | 198 | * Create a list with a given number of elements. This can be used to |
| | 199 | * construct a list element-by-element. |
| | 200 | */ |
| | 201 | CVmObjList::CVmObjList(VMG_ size_t cnt) |
| | 202 | { |
| | 203 | /* allocate space */ |
| | 204 | alloc_list(vmg_ cnt); |
| | 205 | |
| | 206 | /* |
| | 207 | * Clear the list. Since the caller is responsible for populating the |
| | 208 | * list in this version of the constructor, it's possible that GC will |
| | 209 | * run between now and the time the list is fully populated. We must |
| | 210 | * initialize the list to ensure that we don't misinterpret the |
| | 211 | * contents as valid should we run GC between now and the time the |
| | 212 | * caller has finished populating the list. It's adequate to set the |
| | 213 | * list to all zeroes, since we won't try to interpret the contents as |
| | 214 | * valid if the type markers are all invalid. |
| | 215 | */ |
| | 216 | memset(ext_ + VMB_LEN, 0, calc_alloc(cnt) - VMB_LEN); |
| | 217 | } |
| | 218 | |
| | 219 | /* ------------------------------------------------------------------------ */ |
| | 220 | /* |
| | 221 | * allocate space for a list with a given number of elements |
| | 222 | */ |
| | 223 | void CVmObjList::alloc_list(VMG_ size_t cnt) |
| | 224 | { |
| | 225 | size_t alo; |
| | 226 | |
| | 227 | /* calculate the allocation size */ |
| | 228 | alo = calc_alloc(cnt); |
| | 229 | |
| | 230 | /* |
| | 231 | * ensure we're within the limit (NB: this really is 65535 on ALL |
| | 232 | * PLATFORMS - this is a portable limit imposed by the storage format, |
| | 233 | * not a platform-specific size limit |
| | 234 | */ |
| | 235 | if (alo > 65535) |
| | 236 | { |
| | 237 | ext_ = 0; |
| | 238 | err_throw(VMERR_LIST_TOO_LONG); |
| | 239 | } |
| | 240 | |
| | 241 | /* allocate space for the given number of elements */ |
| | 242 | ext_ = (char *)G_mem->get_var_heap()->alloc_mem(alo, this); |
| | 243 | |
| | 244 | /* set the element count */ |
| | 245 | vmb_put_len(ext_, cnt); |
| | 246 | } |
| | 247 | |
| | 248 | /* ------------------------------------------------------------------------ */ |
| | 249 | /* |
| | 250 | * construction: set an element |
| | 251 | */ |
| | 252 | void CVmObjList::cons_set_element(size_t idx, const vm_val_t *val) |
| | 253 | { |
| | 254 | /* set the element's value */ |
| | 255 | vmb_put_dh(get_element_ptr(idx), val); |
| | 256 | } |
| | 257 | |
| | 258 | /* |
| | 259 | * construction: copy a list into our list |
| | 260 | */ |
| | 261 | void CVmObjList::cons_copy_elements(size_t idx, const char *orig_list) |
| | 262 | { |
| | 263 | /* copy the elements */ |
| | 264 | memcpy(get_element_ptr(idx), orig_list + VMB_LEN, |
| | 265 | (vmb_get_len(orig_list) * VMB_DATAHOLDER)); |
| | 266 | } |
| | 267 | |
| | 268 | /* |
| | 269 | * construction: copy element data into our list |
| | 270 | */ |
| | 271 | void CVmObjList::cons_copy_data(size_t idx, const char *ele_array, |
| | 272 | size_t ele_count) |
| | 273 | { |
| | 274 | /* copy the elements */ |
| | 275 | memcpy(get_element_ptr(idx), ele_array, ele_count * VMB_DATAHOLDER); |
| | 276 | } |
| | 277 | |
| | 278 | |
| | 279 | /* ------------------------------------------------------------------------ */ |
| | 280 | /* |
| | 281 | * receive notification of deletion |
| | 282 | */ |
| | 283 | void CVmObjList::notify_delete(VMG_ int in_root_set) |
| | 284 | { |
| | 285 | /* free our extension */ |
| | 286 | if (ext_ != 0 && !in_root_set) |
| | 287 | G_mem->get_var_heap()->free_mem(ext_); |
| | 288 | } |
| | 289 | |
| | 290 | /* ------------------------------------------------------------------------ */ |
| | 291 | /* |
| | 292 | * Set a property. Lists have no settable properties, so simply signal |
| | 293 | * an error indicating that the set-prop call is invalid. |
| | 294 | */ |
| | 295 | void CVmObjList::set_prop(VMG_ CVmUndo *, vm_obj_id_t, |
| | 296 | vm_prop_id_t, const vm_val_t *) |
| | 297 | { |
| | 298 | err_throw(VMERR_INVALID_SETPROP); |
| | 299 | } |
| | 300 | |
| | 301 | /* ------------------------------------------------------------------------ */ |
| | 302 | /* |
| | 303 | * Save the object to a file |
| | 304 | */ |
| | 305 | void CVmObjList::save_to_file(VMG_ CVmFile *fp) |
| | 306 | { |
| | 307 | size_t cnt; |
| | 308 | |
| | 309 | /* get our element count */ |
| | 310 | cnt = vmb_get_len(ext_); |
| | 311 | |
| | 312 | /* write the count and the elements */ |
| | 313 | fp->write_bytes(ext_, calc_alloc(cnt)); |
| | 314 | } |
| | 315 | |
| | 316 | /* |
| | 317 | * Restore the object from a file |
| | 318 | */ |
| | 319 | void CVmObjList::restore_from_file(VMG_ vm_obj_id_t, |
| | 320 | CVmFile *fp, CVmObjFixup *fixups) |
| | 321 | { |
| | 322 | size_t cnt; |
| | 323 | |
| | 324 | /* read the element count */ |
| | 325 | cnt = fp->read_uint2(); |
| | 326 | |
| | 327 | /* free any existing extension */ |
| | 328 | if (ext_ != 0) |
| | 329 | { |
| | 330 | G_mem->get_var_heap()->free_mem(ext_); |
| | 331 | ext_ = 0; |
| | 332 | } |
| | 333 | |
| | 334 | /* allocate the space */ |
| | 335 | alloc_list(vmg_ cnt); |
| | 336 | |
| | 337 | /* store our element count */ |
| | 338 | vmb_put_len(ext_, cnt); |
| | 339 | |
| | 340 | /* read the contents, if there are any elements */ |
| | 341 | fp->read_bytes(ext_ + VMB_LEN, cnt * VMB_DATAHOLDER); |
| | 342 | |
| | 343 | /* fix object references */ |
| | 344 | fixups->fix_dh_array(vmg_ ext_ + VMB_LEN, cnt); |
| | 345 | } |
| | 346 | |
| | 347 | /* ------------------------------------------------------------------------ */ |
| | 348 | /* |
| | 349 | * Mark references |
| | 350 | */ |
| | 351 | void CVmObjList::mark_refs(VMG_ uint state) |
| | 352 | { |
| | 353 | size_t cnt; |
| | 354 | char *p; |
| | 355 | |
| | 356 | /* get my element count */ |
| | 357 | cnt = vmb_get_len(ext_); |
| | 358 | |
| | 359 | /* mark as referenced each object in our list */ |
| | 360 | for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p)) |
| | 361 | { |
| | 362 | /* |
| | 363 | * if this is an object, mark it as referenced, and mark its |
| | 364 | * references as referenced |
| | 365 | */ |
| | 366 | if (vmb_get_dh_type(p) == VM_OBJ) |
| | 367 | G_obj_table->mark_all_refs(vmb_get_dh_obj(p), state); |
| | 368 | } |
| | 369 | } |
| | 370 | |
| | 371 | |
| | 372 | /* ------------------------------------------------------------------------ */ |
| | 373 | /* |
| | 374 | * Add a value to the list. This yields a new list, with the value |
| | 375 | * appended to the existing list. If the value to be appended is itself |
| | 376 | * a list (constant or object), we'll append each element of that list |
| | 377 | * to our list (rather than appending a single element containing a |
| | 378 | * sublist). |
| | 379 | */ |
| | 380 | void CVmObjList::add_val(VMG_ vm_val_t *result, |
| | 381 | vm_obj_id_t self, const vm_val_t *val) |
| | 382 | { |
| | 383 | /* |
| | 384 | * Use the generic list adder, using my extension as the constant |
| | 385 | * list. We store our extension in the general list format required |
| | 386 | * by the static adder. |
| | 387 | */ |
| | 388 | add_to_list(vmg_ result, self, ext_, val); |
| | 389 | } |
| | 390 | |
| | 391 | /* |
| | 392 | * Static list adder. This creates a new list object that results from |
| | 393 | * appending the given value to the given list constant. This is |
| | 394 | * defined statically so that this code can be shared for adding to |
| | 395 | * constant pool lists and adding to CVmObjList objects. |
| | 396 | * |
| | 397 | * 'lstval' must point to a constant list. The first two bytes of the |
| | 398 | * list are stored in portable UINT2 format and give the number of |
| | 399 | * elements in the list; this is immediately followed by a packed array |
| | 400 | * of data holders in portable format. |
| | 401 | * |
| | 402 | * Note that we *always* create a new object to hold the result, even if |
| | 403 | * the new string is identical to the first, so that we consistently |
| | 404 | * return a distinct reference from the original. |
| | 405 | */ |
| | 406 | void CVmObjList::add_to_list(VMG_ vm_val_t *result, |
| | 407 | vm_obj_id_t self, const char *lstval, |
| | 408 | const vm_val_t *rhs) |
| | 409 | { |
| | 410 | size_t lhs_cnt, rhs_cnt; |
| | 411 | vm_obj_id_t obj; |
| | 412 | CVmObjList *objptr; |
| | 413 | size_t i; |
| | 414 | |
| | 415 | /* push self and the other list for protection against GC */ |
| | 416 | G_stk->push()->set_obj(self); |
| | 417 | G_stk->push(rhs); |
| | 418 | |
| | 419 | /* get the number of elements in the left-hand ('self') side */ |
| | 420 | lhs_cnt = vmb_get_len(lstval); |
| | 421 | |
| | 422 | /* get the number of elements the right-hand side concatenates */ |
| | 423 | rhs_cnt = rhs->get_coll_addsub_rhs_ele_cnt(vmg0_); |
| | 424 | |
| | 425 | /* allocate a new object to hold the new list */ |
| | 426 | obj = create(vmg_ FALSE, lhs_cnt + rhs_cnt); |
| | 427 | objptr = (CVmObjList *)vm_objp(vmg_ obj); |
| | 428 | |
| | 429 | /* copy the first list into the new object's list buffer */ |
| | 430 | objptr->cons_copy_elements(0, lstval); |
| | 431 | |
| | 432 | /* add each value from the right-hand side */ |
| | 433 | for (i = 0 ; i < rhs_cnt ; ++i) |
| | 434 | { |
| | 435 | vm_val_t val; |
| | 436 | |
| | 437 | /* retrieve this element of the rhs (adjusting to a 1-based index) */ |
| | 438 | rhs->get_coll_addsub_rhs_ele(vmg_ i + 1, &val); |
| | 439 | |
| | 440 | /* store the element in the new list */ |
| | 441 | objptr->cons_set_element(lhs_cnt + i, &val); |
| | 442 | } |
| | 443 | |
| | 444 | /* set the result to the new list */ |
| | 445 | result->set_obj(obj); |
| | 446 | |
| | 447 | /* discard the GC protection items */ |
| | 448 | G_stk->discard(2); |
| | 449 | } |
| | 450 | |
| | 451 | /* ------------------------------------------------------------------------ */ |
| | 452 | /* |
| | 453 | * Subtract a value from the list. |
| | 454 | */ |
| | 455 | void CVmObjList::sub_val(VMG_ vm_val_t *result, |
| | 456 | vm_obj_id_t self, const vm_val_t *val) |
| | 457 | { |
| | 458 | vm_val_t self_val; |
| | 459 | |
| | 460 | /* |
| | 461 | * Invoke our static list subtraction routine, using our extension |
| | 462 | * as the constant list. Our extension is stored in the same format |
| | 463 | * as a constant list, so we can use the same code to handle |
| | 464 | * subtraction from a list object as we would for subtraction from a |
| | 465 | * constant list. |
| | 466 | */ |
| | 467 | self_val.set_obj(self); |
| | 468 | sub_from_list(vmg_ result, &self_val, ext_, val); |
| | 469 | } |
| | 470 | |
| | 471 | /* |
| | 472 | * Subtract a value from a constant list. |
| | 473 | */ |
| | 474 | void CVmObjList::sub_from_list(VMG_ vm_val_t *result, |
| | 475 | const vm_val_t *lstval, const char *lstmem, |
| | 476 | const vm_val_t *rhs) |
| | 477 | { |
| | 478 | size_t lhs_cnt, rhs_cnt; |
| | 479 | vm_obj_id_t obj; |
| | 480 | CVmObjList *objptr; |
| | 481 | size_t i; |
| | 482 | char *dst; |
| | 483 | size_t dst_cnt; |
| | 484 | |
| | 485 | /* push self and the other list for protection against GC */ |
| | 486 | G_stk->push(lstval); |
| | 487 | G_stk->push(rhs); |
| | 488 | |
| | 489 | /* get the number of elements in the right-hand side */ |
| | 490 | lhs_cnt = vmb_get_len(lstmem); |
| | 491 | |
| | 492 | /* |
| | 493 | * allocate a new object to hold the new list, which will be no |
| | 494 | * bigger than the original left-hand side, since we're doing |
| | 495 | * nothing but (possibly) taking elements out |
| | 496 | */ |
| | 497 | obj = create(vmg_ FALSE, lhs_cnt); |
| | 498 | objptr = (CVmObjList *)vm_objp(vmg_ obj); |
| | 499 | |
| | 500 | /* get the number of elements to consider from the right-hand side */ |
| | 501 | rhs_cnt = rhs->get_coll_addsub_rhs_ele_cnt(vmg0_); |
| | 502 | |
| | 503 | /* copy the first list into the new object's list buffer */ |
| | 504 | objptr->cons_copy_elements(0, lstmem); |
| | 505 | |
| | 506 | /* consider each element of the left-hand side */ |
| | 507 | for (i = 0, dst = objptr->get_element_ptr(0), dst_cnt = 0 ; |
| | 508 | i < lhs_cnt ; ++i) |
| | 509 | { |
| | 510 | vm_val_t src_val; |
| | 511 | int keep; |
| | 512 | size_t j; |
| | 513 | |
| | 514 | /* |
| | 515 | * if our list is from constant memory, get its address again -- |
| | 516 | * the address could have changed due to swapping if we |
| | 517 | * traversed into another list |
| | 518 | */ |
| | 519 | VM_IF_SWAPPING_POOL(if (lstval != 0 && lstval->typ == VM_LIST) |
| | 520 | lstmem = G_const_pool->get_ptr(lstval->val.ofs)); |
| | 521 | |
| | 522 | /* get this element */ |
| | 523 | vmb_get_dh(get_element_ptr_const(lstmem, i), &src_val); |
| | 524 | |
| | 525 | /* presume we'll keep it */ |
| | 526 | keep = TRUE; |
| | 527 | |
| | 528 | /* |
| | 529 | * scan the right side to see if we can find this value - if we |
| | 530 | * can, it's to be removed, so we don't want to copy it to the |
| | 531 | * result list |
| | 532 | */ |
| | 533 | for (j = 0 ; j < rhs_cnt ; ++j) |
| | 534 | { |
| | 535 | vm_val_t rem_val; |
| | 536 | |
| | 537 | /* retrieve this rhs value (using a 1-based index) */ |
| | 538 | rhs->get_coll_addsub_rhs_ele(vmg_ j + 1, &rem_val); |
| | 539 | |
| | 540 | /* if this value matches, we're removing it */ |
| | 541 | if (rem_val.equals(vmg_ &src_val)) |
| | 542 | { |
| | 543 | /* it's to be removed */ |
| | 544 | keep = FALSE; |
| | 545 | |
| | 546 | /* no need to look any further in the rhs list */ |
| | 547 | break; |
| | 548 | } |
| | 549 | } |
| | 550 | |
| | 551 | /* if we're keeping the value, put it in the result list */ |
| | 552 | if (keep) |
| | 553 | { |
| | 554 | /* store it in the result list */ |
| | 555 | vmb_put_dh(dst, &src_val); |
| | 556 | |
| | 557 | /* advance the result pointer */ |
| | 558 | inc_element_ptr(&dst); |
| | 559 | |
| | 560 | /* count it */ |
| | 561 | ++dst_cnt; |
| | 562 | } |
| | 563 | } |
| | 564 | |
| | 565 | /* set the length of the result list */ |
| | 566 | objptr->cons_set_len(dst_cnt); |
| | 567 | |
| | 568 | /* set the result to the new list */ |
| | 569 | result->set_obj(obj); |
| | 570 | |
| | 571 | /* discard the GC protection items */ |
| | 572 | G_stk->discard(2); |
| | 573 | } |
| | 574 | |
| | 575 | /* ------------------------------------------------------------------------ */ |
| | 576 | /* |
| | 577 | * Index the list |
| | 578 | */ |
| | 579 | void CVmObjList::index_val(VMG_ vm_val_t *result, vm_obj_id_t /*self*/, |
| | 580 | const vm_val_t *index_val) |
| | 581 | { |
| | 582 | /* |
| | 583 | * use the constant list indexing routine, using our extension data |
| | 584 | * as the list data |
| | 585 | */ |
| | 586 | index_list(vmg_ result, ext_, index_val); |
| | 587 | } |
| | 588 | |
| | 589 | /* ------------------------------------------------------------------------ */ |
| | 590 | /* |
| | 591 | * Index a constant list |
| | 592 | */ |
| | 593 | void CVmObjList::index_list(VMG_ vm_val_t *result, const char *lst, |
| | 594 | const vm_val_t *index_val) |
| | 595 | { |
| | 596 | uint32 idx; |
| | 597 | |
| | 598 | /* get the index value as an integer */ |
| | 599 | idx = index_val->num_to_int(); |
| | 600 | |
| | 601 | /* index the list */ |
| | 602 | index_list(vmg_ result, lst, idx); |
| | 603 | } |
| | 604 | |
| | 605 | /* |
| | 606 | * Index a constant list by an integer value |
| | 607 | */ |
| | 608 | void CVmObjList::index_list(VMG_ vm_val_t *result, const char *lst, uint idx) |
| | 609 | { |
| | 610 | /* make sure it's in range - 1 to our element count, inclusive */ |
| | 611 | if (idx < 1 || idx > vmb_get_len(lst)) |
| | 612 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 613 | |
| | 614 | /* |
| | 615 | * get the indexed element and store it in the result, adjusting the |
| | 616 | * index to the C-style 0-based range |
| | 617 | */ |
| | 618 | get_element_const(lst, idx - 1, result); |
| | 619 | } |
| | 620 | |
| | 621 | /* ------------------------------------------------------------------------ */ |
| | 622 | /* |
| | 623 | * Set an element of the list |
| | 624 | */ |
| | 625 | void CVmObjList::set_index_val(VMG_ vm_val_t *result, vm_obj_id_t self, |
| | 626 | const vm_val_t *index_val, |
| | 627 | const vm_val_t *new_val) |
| | 628 | { |
| | 629 | /* put the list on the stack to avoid garbage collection */ |
| | 630 | G_stk->push()->set_obj(self); |
| | 631 | |
| | 632 | /* |
| | 633 | * use the constant list set-index routine, using our extension data |
| | 634 | * as the list data |
| | 635 | */ |
| | 636 | set_index_list(vmg_ result, ext_, index_val, new_val); |
| | 637 | |
| | 638 | /* discard the GC protection */ |
| | 639 | G_stk->discard(); |
| | 640 | } |
| | 641 | |
| | 642 | /* ------------------------------------------------------------------------ */ |
| | 643 | /* |
| | 644 | * Set an element in a constant list |
| | 645 | */ |
| | 646 | void CVmObjList::set_index_list(VMG_ vm_val_t *result, const char *lst, |
| | 647 | const vm_val_t *index_val, |
| | 648 | const vm_val_t *new_val) |
| | 649 | { |
| | 650 | uint32 idx; |
| | 651 | CVmObjList *obj; |
| | 652 | |
| | 653 | /* get the index value as an integer */ |
| | 654 | idx = index_val->num_to_int(); |
| | 655 | |
| | 656 | /* push the new value for gc protection during the create */ |
| | 657 | G_stk->push(new_val); |
| | 658 | |
| | 659 | /* make sure it's in range - 1 to our element count, inclusive */ |
| | 660 | if (idx < 1 || idx > vmb_get_len(lst)) |
| | 661 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 662 | |
| | 663 | /* create a new list as a copy of this list */ |
| | 664 | result->set_obj(create(vmg_ FALSE, lst)); |
| | 665 | |
| | 666 | /* get the new list object */ |
| | 667 | obj = (CVmObjList *)vm_objp(vmg_ result->val.obj); |
| | 668 | |
| | 669 | /* update the element of the new list */ |
| | 670 | obj->cons_set_element(idx - 1, new_val); |
| | 671 | |
| | 672 | /* discard our gc protection */ |
| | 673 | G_stk->discard(); |
| | 674 | } |
| | 675 | |
| | 676 | /* ------------------------------------------------------------------------ */ |
| | 677 | /* |
| | 678 | * Check a value for equality |
| | 679 | */ |
| | 680 | int CVmObjList::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, |
| | 681 | int depth) const |
| | 682 | { |
| | 683 | /* if it's a reference to myself, we certainly match */ |
| | 684 | if (val->typ == VM_OBJ && val->val.obj == self) |
| | 685 | return TRUE; |
| | 686 | |
| | 687 | /* |
| | 688 | * compare via the constant list comparison routine, using our |
| | 689 | * extension data as the list data |
| | 690 | */ |
| | 691 | return const_equals(vmg_ 0, ext_, val, depth); |
| | 692 | } |
| | 693 | |
| | 694 | /* ------------------------------------------------------------------------ */ |
| | 695 | /* |
| | 696 | * Constant list comparison routine |
| | 697 | */ |
| | 698 | int CVmObjList::const_equals(VMG_ const vm_val_t *lstval, const char *lstmem, |
| | 699 | const vm_val_t *val, int depth) |
| | 700 | { |
| | 701 | const char *lstmem2; |
| | 702 | const vm_val_t *lstval2; |
| | 703 | size_t cnt; |
| | 704 | size_t idx; |
| | 705 | |
| | 706 | /* get the list underlying the other value */ |
| | 707 | lstmem2 = val->get_as_list(vmg0_); |
| | 708 | lstval2 = val; |
| | 709 | |
| | 710 | /* if it doesn't have an underlying list, it doesn't match */ |
| | 711 | if (lstmem2 == 0) |
| | 712 | return FALSE; |
| | 713 | |
| | 714 | /* if the lists don't have the same length, they don't match */ |
| | 715 | cnt = vmb_get_len(lstmem); |
| | 716 | if (cnt != vmb_get_len(lstmem2)) |
| | 717 | return FALSE; |
| | 718 | |
| | 719 | /* compare each element in the list */ |
| | 720 | for (idx = 0 ; idx < cnt ; ++idx) |
| | 721 | { |
| | 722 | vm_val_t val1; |
| | 723 | vm_val_t val2; |
| | 724 | |
| | 725 | /* |
| | 726 | * if either list comes from constant memory, re-translate its |
| | 727 | * pointer, in case we did any swapping while traversing the |
| | 728 | * previous element |
| | 729 | */ |
| | 730 | VM_IF_SWAPPING_POOL(if (lstval != 0 && lstval->typ == VM_LIST) |
| | 731 | lstmem = G_const_pool->get_ptr(lstval->val.ofs)); |
| | 732 | VM_IF_SWAPPING_POOL(if (lstval2 != 0 && lstval2->typ == VM_LIST) |
| | 733 | lstmem2 = G_const_pool->get_ptr(lstval2->val.ofs)); |
| | 734 | |
| | 735 | /* get the two elements */ |
| | 736 | vmb_get_dh(get_element_ptr_const(lstmem, idx), &val1); |
| | 737 | vmb_get_dh(get_element_ptr_const(lstmem2, idx), &val2); |
| | 738 | |
| | 739 | /* |
| | 740 | * If these elements don't match, the lists don't match. Note that |
| | 741 | * lists can't contain circular references (by their very nature as |
| | 742 | * immutable objects), so we don't need to increase the depth. |
| | 743 | */ |
| | 744 | if (!val1.equals(vmg_ &val2, depth)) |
| | 745 | return FALSE; |
| | 746 | } |
| | 747 | |
| | 748 | /* if we got here, we didn't find any differences, so they match */ |
| | 749 | return TRUE; |
| | 750 | } |
| | 751 | |
| | 752 | /* ------------------------------------------------------------------------ */ |
| | 753 | /* |
| | 754 | * Hash value calculation |
| | 755 | */ |
| | 756 | uint CVmObjList::calc_hash(VMG_ vm_obj_id_t self, int depth) const |
| | 757 | { |
| | 758 | vm_val_t self_val; |
| | 759 | |
| | 760 | /* set up our 'self' value pointer */ |
| | 761 | self_val.set_obj(self); |
| | 762 | |
| | 763 | /* calculate the value */ |
| | 764 | return const_calc_hash(vmg_ &self_val, ext_, depth); |
| | 765 | } |
| | 766 | |
| | 767 | /* |
| | 768 | * Hash value calculation |
| | 769 | */ |
| | 770 | uint CVmObjList::const_calc_hash(VMG_ const vm_val_t *self_val, |
| | 771 | const char *lst, int depth) |
| | 772 | { |
| | 773 | size_t len; |
| | 774 | size_t i; |
| | 775 | uint hash; |
| | 776 | |
| | 777 | /* get and skip the length prefix */ |
| | 778 | len = vmb_get_len(lst); |
| | 779 | |
| | 780 | /* calculate a hash combining the hash of each element in the list */ |
| | 781 | for (hash = 0, i = 0 ; i < len ; ++i) |
| | 782 | { |
| | 783 | vm_val_t ele; |
| | 784 | |
| | 785 | /* re-translate in case of swapping */ |
| | 786 | VM_IF_SWAPPING_POOL(if (self_val->typ == VM_LIST) |
| | 787 | lst = G_const_pool->get_ptr(self_val->val.ofs)); |
| | 788 | |
| | 789 | /* get this element */ |
| | 790 | get_element_const(lst, i, &ele); |
| | 791 | |
| | 792 | /* |
| | 793 | * Compute its hash value and add it into the total. Note that |
| | 794 | * even though we're recursively calculating the hash of an |
| | 795 | * element, we don't need to increase the recursion depth, because |
| | 796 | * it's impossible for a list to have cycles. |
| | 797 | * |
| | 798 | * (It's not possible for a list to have cycles because a list is |
| | 799 | * always constructed with its contents, and can never be changed. |
| | 800 | * This means that there's no possibility of storing a reference to |
| | 801 | * the new list inside the list itself, or inside any other list |
| | 802 | * the list refers to. It *is* possible to put the reference to |
| | 803 | * the new list in a mutable object to which the list refers, but |
| | 804 | * in such cases, that mutable object will be capable of having |
| | 805 | * cycles in its references, so it will be responsible for |
| | 806 | * increasing the depth counter when it recurses.) |
| | 807 | */ |
| | 808 | hash += ele.calc_hash(vmg_ depth); |
| | 809 | } |
| | 810 | |
| | 811 | /* return the hash value */ |
| | 812 | return hash; |
| | 813 | } |
| | 814 | |
| | 815 | |
| | 816 | /* ------------------------------------------------------------------------ */ |
| | 817 | /* |
| | 818 | * Find a value in a list |
| | 819 | */ |
| | 820 | int CVmObjList::find_in_list(VMG_ const vm_val_t *lst, |
| | 821 | const vm_val_t *val, size_t *idxp) |
| | 822 | { |
| | 823 | const char *lstmem; |
| | 824 | size_t cnt; |
| | 825 | size_t idx; |
| | 826 | |
| | 827 | /* get the list underyling this value */ |
| | 828 | lstmem = lst->get_as_list(vmg0_); |
| | 829 | |
| | 830 | /* get the length of the list */ |
| | 831 | cnt = vmb_get_len(lstmem); |
| | 832 | |
| | 833 | /* scan the list for the value */ |
| | 834 | for (idx = 0 ; idx < cnt ; ++idx) |
| | 835 | { |
| | 836 | vm_val_t curval; |
| | 837 | |
| | 838 | /* |
| | 839 | * re-translate the list pointer if it's in constant memory, in |
| | 840 | * case we did any swapping on the last iteratino |
| | 841 | */ |
| | 842 | VM_IF_SWAPPING_POOL(if (lst->typ == VM_LIST) |
| | 843 | lstmem = G_const_pool->get_ptr(lst->val.ofs)); |
| | 844 | |
| | 845 | /* get this list element */ |
| | 846 | vmb_get_dh(get_element_ptr_const(lstmem, idx), &curval); |
| | 847 | |
| | 848 | /* compare this value to the one we're looking for */ |
| | 849 | if (curval.equals(vmg_ val)) |
| | 850 | { |
| | 851 | /* this is the one - set the return index */ |
| | 852 | if (idxp != 0) |
| | 853 | *idxp = idx; |
| | 854 | |
| | 855 | /* indicate that we found the value */ |
| | 856 | return TRUE; |
| | 857 | } |
| | 858 | } |
| | 859 | |
| | 860 | /* we didn't find the value */ |
| | 861 | return FALSE; |
| | 862 | } |
| | 863 | |
| | 864 | /* |
| | 865 | * Find the last match for a value in a list |
| | 866 | */ |
| | 867 | int CVmObjList::find_last_in_list(VMG_ const vm_val_t *lst, |
| | 868 | const vm_val_t *val, size_t *idxp) |
| | 869 | { |
| | 870 | const char *lstmem; |
| | 871 | size_t cnt; |
| | 872 | size_t idx; |
| | 873 | |
| | 874 | /* get the list underyling this value */ |
| | 875 | lstmem = lst->get_as_list(vmg0_); |
| | 876 | |
| | 877 | /* get the length of the list */ |
| | 878 | cnt = vmb_get_len(lstmem); |
| | 879 | |
| | 880 | /* scan the list for the value */ |
| | 881 | for (idx = cnt ; idx != 0 ; --idx) |
| | 882 | { |
| | 883 | vm_val_t curval; |
| | 884 | |
| | 885 | /* |
| | 886 | * re-translate the list pointer if it's in constant memory, in |
| | 887 | * case we did any swapping on the last iteratino |
| | 888 | */ |
| | 889 | VM_IF_SWAPPING_POOL(if (lst->typ == VM_LIST) |
| | 890 | lstmem = G_const_pool->get_ptr(lst->val.ofs)); |
| | 891 | |
| | 892 | /* get this list element */ |
| | 893 | vmb_get_dh(get_element_ptr_const(lstmem, idx), &curval); |
| | 894 | |
| | 895 | /* compare this value to the one we're looking for */ |
| | 896 | if (curval.equals(vmg_ val)) |
| | 897 | { |
| | 898 | /* this is the one - set the return index */ |
| | 899 | if (idxp != 0) |
| | 900 | *idxp = idx; |
| | 901 | |
| | 902 | /* indicate that we found the value */ |
| | 903 | return TRUE; |
| | 904 | } |
| | 905 | } |
| | 906 | |
| | 907 | /* we didn't find the value */ |
| | 908 | return FALSE; |
| | 909 | } |
| | 910 | |
| | 911 | /* ------------------------------------------------------------------------ */ |
| | 912 | /* |
| | 913 | * Compute the intersection of two lists. Returns a new list with the |
| | 914 | * elements that occur in both lists. |
| | 915 | */ |
| | 916 | vm_obj_id_t CVmObjList::intersect(VMG_ const vm_val_t *lst1, |
| | 917 | const vm_val_t *lst2) |
| | 918 | { |
| | 919 | const char *lstmem1; |
| | 920 | const char *lstmem2; |
| | 921 | size_t cnt1; |
| | 922 | size_t cnt2; |
| | 923 | size_t idx; |
| | 924 | vm_obj_id_t resobj; |
| | 925 | CVmObjList *reslst; |
| | 926 | size_t residx; |
| | 927 | |
| | 928 | /* get the two list memory blocks */ |
| | 929 | lstmem1 = lst1->get_as_list(vmg0_); |
| | 930 | lstmem2 = lst2->get_as_list(vmg0_); |
| | 931 | |
| | 932 | /* get the lengths of the lists */ |
| | 933 | cnt1 = vmb_get_len(lstmem1); |
| | 934 | cnt2 = vmb_get_len(lstmem2); |
| | 935 | |
| | 936 | /* if the first list is larger than the second, swap them */ |
| | 937 | if (cnt1 > cnt2) |
| | 938 | { |
| | 939 | const vm_val_t *tmp; |
| | 940 | |
| | 941 | /* swap the vm_val_t pointers */ |
| | 942 | tmp = lst1; |
| | 943 | lst1 = lst2; |
| | 944 | lst2 = tmp; |
| | 945 | |
| | 946 | /* |
| | 947 | * momentarily forget the larger count and memory pointer, and |
| | 948 | * copy the smaller count into cnt1 |
| | 949 | */ |
| | 950 | cnt1 = cnt2; |
| | 951 | lstmem1 = lstmem2; |
| | 952 | } |
| | 953 | |
| | 954 | /* |
| | 955 | * Allocate our result list. The result list can't have any more |
| | 956 | * elements in it than the shorter of the two lists, whose length is |
| | 957 | * now in cnt1. |
| | 958 | */ |
| | 959 | resobj = create(vmg_ FALSE, cnt1); |
| | 960 | reslst = (CVmObjList *)vm_objp(vmg_ resobj); |
| | 961 | |
| | 962 | /* we haven't put any elements in the result list yet */ |
| | 963 | residx = 0; |
| | 964 | |
| | 965 | /* |
| | 966 | * for each element in the first list, find the element in the |
| | 967 | * second list |
| | 968 | */ |
| | 969 | for (idx = 0 ; idx < cnt1 ; ++idx) |
| | 970 | { |
| | 971 | vm_val_t curval; |
| | 972 | |
| | 973 | /* re-translate the first list address in case of swapping */ |
| | 974 | if (lst1->typ == VM_LIST) |
| | 975 | lstmem1 = lst1->get_as_list(vmg0_); |
| | 976 | |
| | 977 | /* get this element from the first list */ |
| | 978 | vmb_get_dh(get_element_ptr_const(lstmem1, idx), &curval); |
| | 979 | |
| | 980 | /* find the element in the second list */ |
| | 981 | if (find_in_list(vmg_ lst2, &curval, 0)) |
| | 982 | { |
| | 983 | /* we found it - copy it into the result list */ |
| | 984 | reslst->cons_set_element(residx, &curval); |
| | 985 | |
| | 986 | /* count the new entry in the result list */ |
| | 987 | ++residx; |
| | 988 | } |
| | 989 | } |
| | 990 | |
| | 991 | /* |
| | 992 | * set the actual result length, which might be shorter than the |
| | 993 | * amount we allocated |
| | 994 | */ |
| | 995 | reslst->cons_set_len(residx); |
| | 996 | |
| | 997 | /* return the result list */ |
| | 998 | return resobj; |
| | 999 | } |
| | 1000 | |
| | 1001 | /* ------------------------------------------------------------------------ */ |
| | 1002 | /* |
| | 1003 | * Uniquify the list; modifies the list in place, so this can only be |
| | 1004 | * used during construction of a new list |
| | 1005 | */ |
| | 1006 | void CVmObjList::cons_uniquify(VMG0_) |
| | 1007 | { |
| | 1008 | size_t cnt; |
| | 1009 | size_t src, dst; |
| | 1010 | |
| | 1011 | /* get the length of the list */ |
| | 1012 | cnt = vmb_get_len(ext_); |
| | 1013 | |
| | 1014 | /* loop through the list and look for repeated values */ |
| | 1015 | for (src = dst = 0 ; src < cnt ; ++src) |
| | 1016 | { |
| | 1017 | size_t idx; |
| | 1018 | vm_val_t src_val; |
| | 1019 | int found; |
| | 1020 | |
| | 1021 | /* |
| | 1022 | * look for a copy of this source value already in the output |
| | 1023 | * list |
| | 1024 | */ |
| | 1025 | index_list(vmg_ &src_val, ext_, src + 1); |
| | 1026 | for (idx = 0, found = FALSE ; idx < dst ; ++idx) |
| | 1027 | { |
| | 1028 | vm_val_t idx_val; |
| | 1029 | |
| | 1030 | /* get this value */ |
| | 1031 | index_list(vmg_ &idx_val, ext_, idx + 1); |
| | 1032 | |
| | 1033 | /* if it's equal to the current source value, note it */ |
| | 1034 | if (src_val.equals(vmg_ &idx_val)) |
| | 1035 | { |
| | 1036 | /* note that we found it */ |
| | 1037 | found = TRUE; |
| | 1038 | |
| | 1039 | /* no need to look any further */ |
| | 1040 | break; |
| | 1041 | } |
| | 1042 | } |
| | 1043 | |
| | 1044 | /* if we didn't find the value, copy it to the output list */ |
| | 1045 | if (!found) |
| | 1046 | { |
| | 1047 | /* add it to the output list */ |
| | 1048 | cons_set_element(dst, &src_val); |
| | 1049 | |
| | 1050 | /* count it */ |
| | 1051 | ++dst; |
| | 1052 | } |
| | 1053 | } |
| | 1054 | |
| | 1055 | /* adjust the size of the result list */ |
| | 1056 | cons_set_len(dst); |
| | 1057 | } |
| | 1058 | |
| | 1059 | /* ------------------------------------------------------------------------ */ |
| | 1060 | /* |
| | 1061 | * Create an iterator |
| | 1062 | */ |
| | 1063 | void CVmObjList::new_iterator(VMG_ vm_val_t *retval, |
| | 1064 | const vm_val_t *self_val) |
| | 1065 | { |
| | 1066 | size_t len; |
| | 1067 | |
| | 1068 | /* get the number of elements in the list */ |
| | 1069 | len = vmb_get_len(self_val->get_as_list(vmg0_)); |
| | 1070 | |
| | 1071 | /* |
| | 1072 | * Set up a new indexed iterator object. The first valid index for |
| | 1073 | * a list is always 1, and the last valid index is the same as the |
| | 1074 | * number of elements in the list. |
| | 1075 | */ |
| | 1076 | retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ self_val, 1, len)); |
| | 1077 | } |
| | 1078 | |
| | 1079 | /* ------------------------------------------------------------------------ */ |
| | 1080 | /* |
| | 1081 | * Evaluate a property |
| | 1082 | */ |
| | 1083 | int CVmObjList::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, |
| | 1084 | vm_obj_id_t self, vm_obj_id_t *source_obj, |
| | 1085 | uint *argc) |
| | 1086 | { |
| | 1087 | vm_val_t self_val; |
| | 1088 | |
| | 1089 | /* use the constant evaluator */ |
| | 1090 | self_val.set_obj(self); |
| | 1091 | if (const_get_prop(vmg_ retval, &self_val, ext_, prop, source_obj, argc)) |
| | 1092 | { |
| | 1093 | *source_obj = metaclass_reg_->get_class_obj(vmg0_); |
| | 1094 | return TRUE; |
| | 1095 | } |
| | 1096 | |
| | 1097 | /* inherit default handling from the base object class */ |
| | 1098 | return CVmObjCollection::get_prop(vmg_ prop, retval, self, |
| | 1099 | source_obj, argc); |
| | 1100 | } |
| | 1101 | |
| | 1102 | /* ------------------------------------------------------------------------ */ |
| | 1103 | /* |
| | 1104 | * Evaluate a property of a constant list value |
| | 1105 | */ |
| | 1106 | int CVmObjList::const_get_prop(VMG_ vm_val_t *retval, |
| | 1107 | const vm_val_t *self_val, const char *lst, |
| | 1108 | vm_prop_id_t prop, vm_obj_id_t *src_obj, |
| | 1109 | uint *argc) |
| | 1110 | { |
| | 1111 | uint func_idx; |
| | 1112 | |
| | 1113 | /* presume no source object */ |
| | 1114 | *src_obj = VM_INVALID_OBJ; |
| | 1115 | |
| | 1116 | /* translate the property index to an index into our function table */ |
| | 1117 | func_idx = G_meta_table |
| | 1118 | ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); |
| | 1119 | |
| | 1120 | /* call the appropriate function */ |
| | 1121 | if ((*func_table_[func_idx])(vmg_ retval, self_val, lst, argc)) |
| | 1122 | return TRUE; |
| | 1123 | |
| | 1124 | /* |
| | 1125 | * If this is a constant list (which is indicated by a non-object type |
| | 1126 | * 'self'), try inheriting the default object interpretation, passing |
| | 1127 | * the constant list placeholder object for its type information. |
| | 1128 | */ |
| | 1129 | if (self_val->typ != VM_OBJ) |
| | 1130 | { |
| | 1131 | /* try going to our base class, CVmCollection */ |
| | 1132 | if (((CVmObjCollection *)vm_objp(vmg_ G_predef->const_lst_obj)) |
| | 1133 | ->const_get_coll_prop(vmg_ prop, retval, self_val, src_obj, argc)) |
| | 1134 | return TRUE; |
| | 1135 | |
| | 1136 | /* try going to our next base class, CVmObject */ |
| | 1137 | if (vm_objp(vmg_ G_predef->const_lst_obj) |
| | 1138 | ->CVmObject::get_prop(vmg_ prop, retval, G_predef->const_lst_obj, |
| | 1139 | src_obj, argc)) |
| | 1140 | return TRUE; |
| | 1141 | } |
| | 1142 | |
| | 1143 | /* not handled */ |
| | 1144 | return FALSE; |
| | 1145 | } |
| | 1146 | |
| | 1147 | /* ------------------------------------------------------------------------ */ |
| | 1148 | /* |
| | 1149 | * property evaluator - select a subset through a callback |
| | 1150 | */ |
| | 1151 | int CVmObjList::getp_subset(VMG_ vm_val_t *retval, const vm_val_t *self_val, |
| | 1152 | const char *lst, uint *argc) |
| | 1153 | { |
| | 1154 | const vm_val_t *func_val; |
| | 1155 | size_t src; |
| | 1156 | size_t dst; |
| | 1157 | size_t cnt; |
| | 1158 | char *new_lst; |
| | 1159 | CVmObjList *new_lst_obj; |
| | 1160 | static CVmNativeCodeDesc desc(1); |
| | 1161 | |
| | 1162 | /* check arguments */ |
| | 1163 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1164 | return TRUE; |
| | 1165 | |
| | 1166 | /* get the function pointer argument, but leave it on the stack */ |
| | 1167 | func_val = G_stk->get(0); |
| | 1168 | |
| | 1169 | /* push a self-reference while allocating to protect from gc */ |
| | 1170 | G_stk->push(self_val); |
| | 1171 | |
| | 1172 | /* |
| | 1173 | * Make a copy of our list for the return value. The result value |
| | 1174 | * will be at most the same size as our current list; since we have |
| | 1175 | * no way of knowing exactly how large it will be, and since we |
| | 1176 | * don't want to run through the selection functions twice, we'll |
| | 1177 | * just allocate at the maximum size and leave it partially unused |
| | 1178 | * if we don't need all of the space. By making a copy of the input |
| | 1179 | * list, we also can avoid worrying about whether the input list was |
| | 1180 | * a constant, and hence we don't have to worry about the |
| | 1181 | * possibility of constant page swapping. |
| | 1182 | */ |
| | 1183 | retval->set_obj(create(vmg_ FALSE, lst)); |
| | 1184 | |
| | 1185 | /* get the return value list data */ |
| | 1186 | new_lst_obj = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 1187 | new_lst = new_lst_obj->ext_; |
| | 1188 | |
| | 1189 | /* get the length of the list */ |
| | 1190 | cnt = vmb_get_len(new_lst); |
| | 1191 | |
| | 1192 | /* |
| | 1193 | * push a reference to the new list to protect it from the garbage |
| | 1194 | * collector, which could be invoked in the course of executing the |
| | 1195 | * user callback |
| | 1196 | */ |
| | 1197 | G_stk->push(retval); |
| | 1198 | |
| | 1199 | /* |
| | 1200 | * Go through each element of our list, and invoke the callback on |
| | 1201 | * each element. If the element passes, write it to the current |
| | 1202 | * output location in the list; otherwise, just skip it. |
| | 1203 | * |
| | 1204 | * Note that we're using the same list as source and destination, |
| | 1205 | * which is easy because the list will either shrink or stay the |
| | 1206 | * same - we'll never need to insert new elements. |
| | 1207 | */ |
| | 1208 | for (src = dst = 0 ; src < cnt ; ++src) |
| | 1209 | { |
| | 1210 | vm_val_t ele; |
| | 1211 | const vm_val_t *val; |
| | 1212 | |
| | 1213 | /* |
| | 1214 | * get this element (using a 1-based index), and push it as the |
| | 1215 | * callback's argument |
| | 1216 | */ |
| | 1217 | index_list(vmg_ &ele, new_lst, src + 1); |
| | 1218 | G_stk->push(&ele); |
| | 1219 | |
| | 1220 | /* invoke the callback */ |
| | 1221 | G_interpreter->call_func_ptr(vmg_ func_val, 1, "list.subset", 0); |
| | 1222 | |
| | 1223 | /* get the result from R0 */ |
| | 1224 | val = G_interpreter->get_r0(); |
| | 1225 | |
| | 1226 | /* |
| | 1227 | * if the callback returned non-nil and non-zero, include this |
| | 1228 | * element in the result |
| | 1229 | */ |
| | 1230 | if (val->typ == VM_NIL |
| | 1231 | || (val->typ == VM_INT && val->val.intval == 0)) |
| | 1232 | { |
| | 1233 | /* it's nil or zero - don't include it in the result */ |
| | 1234 | } |
| | 1235 | else |
| | 1236 | { |
| | 1237 | /* include this element in the result */ |
| | 1238 | new_lst_obj->cons_set_element(dst, &ele); |
| | 1239 | |
| | 1240 | /* advance the output index */ |
| | 1241 | ++dst; |
| | 1242 | } |
| | 1243 | } |
| | 1244 | |
| | 1245 | /* |
| | 1246 | * set the result list length to the number of elements we actually |
| | 1247 | * copied |
| | 1248 | */ |
| | 1249 | new_lst_obj->cons_set_len(dst); |
| | 1250 | |
| | 1251 | /* discard our gc protection (self, return value) and our arguments */ |
| | 1252 | G_stk->discard(3); |
| | 1253 | |
| | 1254 | /* handled */ |
| | 1255 | return TRUE; |
| | 1256 | } |
| | 1257 | |
| | 1258 | |
| | 1259 | /* ------------------------------------------------------------------------ */ |
| | 1260 | /* |
| | 1261 | * property evaluator - map through a callback |
| | 1262 | */ |
| | 1263 | int CVmObjList::getp_map(VMG_ vm_val_t *retval, const vm_val_t *self_val, |
| | 1264 | const char *lst, uint *argc) |
| | 1265 | { |
| | 1266 | const vm_val_t *func_val; |
| | 1267 | size_t cnt; |
| | 1268 | size_t idx; |
| | 1269 | char *new_lst; |
| | 1270 | CVmObjList *new_lst_obj; |
| | 1271 | static CVmNativeCodeDesc desc(1); |
| | 1272 | |
| | 1273 | /* check arguments */ |
| | 1274 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1275 | return TRUE; |
| | 1276 | |
| | 1277 | /* get the function pointer argument, but leave it on the stack */ |
| | 1278 | func_val = G_stk->get(0); |
| | 1279 | |
| | 1280 | /* push a self-reference while allocating to protect from gc */ |
| | 1281 | G_stk->push(self_val); |
| | 1282 | |
| | 1283 | /* |
| | 1284 | * Make a copy of our list for the return value, since the result |
| | 1285 | * value is always the same size as our current list. By making a |
| | 1286 | * copy of the input list, we also can avoid worrying about whether |
| | 1287 | * the input list was a constant, and hence we don't have to worry |
| | 1288 | * about the possibility of constant page swapping - we'll just |
| | 1289 | * update elements of the copy in-place. |
| | 1290 | */ |
| | 1291 | retval->set_obj(create(vmg_ FALSE, lst)); |
| | 1292 | |
| | 1293 | /* get the return value list data */ |
| | 1294 | new_lst_obj = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 1295 | new_lst = new_lst_obj->ext_; |
| | 1296 | |
| | 1297 | /* get the length of the list */ |
| | 1298 | cnt = vmb_get_len(new_lst); |
| | 1299 | |
| | 1300 | /* |
| | 1301 | * push a reference to the new list to protect it from the garbage |
| | 1302 | * collector, which could be invoked in the course of executing the |
| | 1303 | * user callback |
| | 1304 | */ |
| | 1305 | G_stk->push(retval); |
| | 1306 | |
| | 1307 | /* |
| | 1308 | * Go through each element of our list, and invoke the callback on |
| | 1309 | * each element. Replace each element with the result of the |
| | 1310 | * callback. |
| | 1311 | */ |
| | 1312 | for (idx = 0 ; idx < cnt ; ++idx) |
| | 1313 | { |
| | 1314 | /* |
| | 1315 | * get this element (using a 1-based index), and push it as the |
| | 1316 | * callback's argument |
| | 1317 | */ |
| | 1318 | index_and_push(vmg_ new_lst, idx + 1); |
| | 1319 | |
| | 1320 | /* invoke the callback */ |
| | 1321 | G_interpreter->call_func_ptr(vmg_ func_val, 1, "list.mapAll", 0); |
| | 1322 | |
| | 1323 | /* store the result in the list */ |
| | 1324 | new_lst_obj->cons_set_element(idx, G_interpreter->get_r0()); |
| | 1325 | } |
| | 1326 | |
| | 1327 | /* discard our gc protection (self, return value) and our arguments */ |
| | 1328 | G_stk->discard(3); |
| | 1329 | |
| | 1330 | /* handled */ |
| | 1331 | return TRUE; |
| | 1332 | } |
| | 1333 | |
| | 1334 | /* ------------------------------------------------------------------------ */ |
| | 1335 | /* |
| | 1336 | * property evaluator - length |
| | 1337 | */ |
| | 1338 | int CVmObjList::getp_len(VMG_ vm_val_t *retval, const vm_val_t *self_val, |
| | 1339 | const char *lst, uint *argc) |
| | 1340 | { |
| | 1341 | static CVmNativeCodeDesc desc(0); |
| | 1342 | |
| | 1343 | /* check arguments */ |
| | 1344 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1345 | return TRUE; |
| | 1346 | |
| | 1347 | /* return the element count */ |
| | 1348 | retval->set_int(vmb_get_len(lst)); |
| | 1349 | |
| | 1350 | /* handled */ |
| | 1351 | return TRUE; |
| | 1352 | } |
| | 1353 | |
| | 1354 | /* ------------------------------------------------------------------------ */ |
| | 1355 | /* |
| | 1356 | * property evaluator - sublist |
| | 1357 | */ |
| | 1358 | int CVmObjList::getp_sublist(VMG_ vm_val_t *retval, const vm_val_t *self_val, |
| | 1359 | const char *lst, uint *in_argc) |
| | 1360 | { |
| | 1361 | uint argc = (in_argc != 0 ? *in_argc : 0); |
| | 1362 | ulong start; |
| | 1363 | ulong len; |
| | 1364 | vm_obj_id_t obj; |
| | 1365 | size_t old_cnt; |
| | 1366 | size_t new_cnt; |
| | 1367 | static CVmNativeCodeDesc desc(1, 1); |
| | 1368 | |
| | 1369 | /* check arguments */ |
| | 1370 | if (get_prop_check_argc(retval, in_argc, &desc)) |
| | 1371 | return TRUE; |
| | 1372 | |
| | 1373 | /* get the original element count */ |
| | 1374 | old_cnt = vmb_get_len(lst); |
| | 1375 | |
| | 1376 | /* pop the starting index */ |
| | 1377 | start = CVmBif::pop_long_val(vmg0_); |
| | 1378 | |
| | 1379 | /* |
| | 1380 | * pop the length, if present; if not, use the current element |
| | 1381 | * count, which will ensure that we use all available elements of |
| | 1382 | * the sublist |
| | 1383 | */ |
| | 1384 | if (argc >= 2) |
| | 1385 | len = CVmBif::pop_long_val(vmg0_); |
| | 1386 | else |
| | 1387 | len = old_cnt; |
| | 1388 | |
| | 1389 | /* push the 'self' as protection from GC */ |
| | 1390 | G_stk->push(self_val); |
| | 1391 | |
| | 1392 | /* skip to the first element */ |
| | 1393 | lst += VMB_LEN; |
| | 1394 | |
| | 1395 | /* skip to the desired first element */ |
| | 1396 | if (start >= 1 && start <= old_cnt) |
| | 1397 | { |
| | 1398 | /* it's in range - skip to the desired first element */ |
| | 1399 | lst += (start - 1) * VMB_DATAHOLDER; |
| | 1400 | new_cnt = old_cnt - (start - 1); |
| | 1401 | } |
| | 1402 | else |
| | 1403 | { |
| | 1404 | /* there's nothing left */ |
| | 1405 | new_cnt = 0; |
| | 1406 | } |
| | 1407 | |
| | 1408 | /* |
| | 1409 | * limit the result to the desired new count, if it's shorter than |
| | 1410 | * what we have left (we obviously can't give them more elements |
| | 1411 | * than we have remaining) |
| | 1412 | */ |
| | 1413 | if (len < new_cnt) |
| | 1414 | new_cnt = (size_t)len; |
| | 1415 | |
| | 1416 | /* create the new list */ |
| | 1417 | obj = create(vmg_ FALSE, new_cnt); |
| | 1418 | |
| | 1419 | /* copy the elements */ |
| | 1420 | ((CVmObjList *)vm_objp(vmg_ obj))->cons_copy_data(0, lst, new_cnt); |
| | 1421 | |
| | 1422 | /* return the new object */ |
| | 1423 | retval->set_obj(obj); |
| | 1424 | |
| | 1425 | /* discard GC protection */ |
| | 1426 | G_stk->discard(); |
| | 1427 | |
| | 1428 | /* handled */ |
| | 1429 | return TRUE; |
| | 1430 | } |
| | 1431 | |
| | 1432 | /* ------------------------------------------------------------------------ */ |
| | 1433 | /* |
| | 1434 | * property evaluator - intersect |
| | 1435 | */ |
| | 1436 | int CVmObjList::getp_intersect(VMG_ vm_val_t *retval, |
| | 1437 | const vm_val_t *self_val, |
| | 1438 | const char *lst, uint *argc) |
| | 1439 | { |
| | 1440 | vm_val_t val2; |
| | 1441 | vm_obj_id_t obj; |
| | 1442 | static CVmNativeCodeDesc desc(1); |
| | 1443 | |
| | 1444 | /* check arguments */ |
| | 1445 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1446 | return TRUE; |
| | 1447 | |
| | 1448 | /* get the second list, but leave it on the stack for GC protection */ |
| | 1449 | val2 = *G_stk->get(0); |
| | 1450 | |
| | 1451 | /* put myself on the stack for GC protection as well */ |
| | 1452 | G_stk->push(self_val); |
| | 1453 | |
| | 1454 | /* make sure the other value is indeed a list */ |
| | 1455 | if (val2.get_as_list(vmg0_) == 0) |
| | 1456 | err_throw(VMERR_LIST_VAL_REQD); |
| | 1457 | |
| | 1458 | /* compute the intersection */ |
| | 1459 | obj = intersect(vmg_ self_val, &val2); |
| | 1460 | |
| | 1461 | /* discard the argument lists */ |
| | 1462 | G_stk->discard(2); |
| | 1463 | |
| | 1464 | /* return the new object */ |
| | 1465 | retval->set_obj(obj); |
| | 1466 | |
| | 1467 | /* handled */ |
| | 1468 | return TRUE; |
| | 1469 | } |
| | 1470 | |
| | 1471 | /* ------------------------------------------------------------------------ */ |
| | 1472 | /* |
| | 1473 | * property evaluator - indexOf |
| | 1474 | */ |
| | 1475 | int CVmObjList::getp_index_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, |
| | 1476 | const char *lst, uint *argc) |
| | 1477 | { |
| | 1478 | vm_val_t subval; |
| | 1479 | size_t idx; |
| | 1480 | static CVmNativeCodeDesc desc(1); |
| | 1481 | |
| | 1482 | /* check arguments */ |
| | 1483 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1484 | return TRUE; |
| | 1485 | |
| | 1486 | /* pop the value to find */ |
| | 1487 | G_stk->pop(&subval); |
| | 1488 | |
| | 1489 | /* find the value in the list */ |
| | 1490 | if (find_in_list(vmg_ self_val, &subval, &idx)) |
| | 1491 | { |
| | 1492 | /* found it - adjust to 1-based index for return */ |
| | 1493 | retval->set_int(idx + 1); |
| | 1494 | } |
| | 1495 | else |
| | 1496 | { |
| | 1497 | /* didn't find it - return nil */ |
| | 1498 | retval->set_nil(); |
| | 1499 | } |
| | 1500 | |
| | 1501 | /* handled */ |
| | 1502 | return TRUE; |
| | 1503 | } |
| | 1504 | |
| | 1505 | /* ------------------------------------------------------------------------ */ |
| | 1506 | /* |
| | 1507 | * property evaluator - car |
| | 1508 | */ |
| | 1509 | int CVmObjList::getp_car(VMG_ vm_val_t *retval, const vm_val_t *self_val, |
| | 1510 | const char *lst, uint *argc) |
| | 1511 | { |
| | 1512 | static CVmNativeCodeDesc desc(0); |
| | 1513 | |
| | 1514 | /* check arguments */ |
| | 1515 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1516 | return TRUE; |
| | 1517 | |
| | 1518 | /* |
| | 1519 | * if the list has at least one element, return it; otherwise return |
| | 1520 | * nil |
| | 1521 | */ |
| | 1522 | if (vmb_get_len(lst) == 0) |
| | 1523 | { |
| | 1524 | /* no elements - return nil */ |
| | 1525 | retval->set_nil(); |
| | 1526 | } |
| | 1527 | else |
| | 1528 | { |
| | 1529 | /* it has at least one element - return the first element */ |
| | 1530 | vmb_get_dh(lst + VMB_LEN, retval); |
| | 1531 | } |
| | 1532 | |
| | 1533 | /* handled */ |
| | 1534 | return TRUE; |
| | 1535 | } |
| | 1536 | |
| | 1537 | /* ------------------------------------------------------------------------ */ |
| | 1538 | /* |
| | 1539 | * property evaluator - cdr |
| | 1540 | */ |
| | 1541 | int CVmObjList::getp_cdr(VMG_ vm_val_t *retval, const vm_val_t *self_val, |
| | 1542 | const char *lst, uint *argc) |
| | 1543 | { |
| | 1544 | static CVmNativeCodeDesc desc(0); |
| | 1545 | |
| | 1546 | /* check arguments */ |
| | 1547 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1548 | return TRUE; |
| | 1549 | |
| | 1550 | /* push a self-reference for GC protection */ |
| | 1551 | G_stk->push(self_val); |
| | 1552 | |
| | 1553 | /* |
| | 1554 | * if the list has no elements, return nil; otherwise, return the |
| | 1555 | * sublist starting with the second element (thus return an empty |
| | 1556 | * list if the original list has only one element) |
| | 1557 | */ |
| | 1558 | if (vmb_get_len(lst) == 0) |
| | 1559 | { |
| | 1560 | /* no elements - return nil */ |
| | 1561 | retval->set_nil(); |
| | 1562 | } |
| | 1563 | else |
| | 1564 | { |
| | 1565 | vm_obj_id_t obj; |
| | 1566 | size_t new_cnt; |
| | 1567 | |
| | 1568 | /* reduce the list count by one */ |
| | 1569 | new_cnt = vmb_get_len(lst) - 1; |
| | 1570 | |
| | 1571 | /* skip past the first element */ |
| | 1572 | lst += VMB_LEN + VMB_DATAHOLDER; |
| | 1573 | |
| | 1574 | /* create the new list */ |
| | 1575 | obj = create(vmg_ FALSE, new_cnt); |
| | 1576 | |
| | 1577 | /* copy the elements */ |
| | 1578 | ((CVmObjList *)vm_objp(vmg_ obj))->cons_copy_data(0, lst, new_cnt); |
| | 1579 | |
| | 1580 | /* return the new object */ |
| | 1581 | retval->set_obj(obj); |
| | 1582 | } |
| | 1583 | |
| | 1584 | /* discard the stack protection */ |
| | 1585 | G_stk->discard(); |
| | 1586 | |
| | 1587 | /* handled */ |
| | 1588 | return TRUE; |
| | 1589 | } |
| | 1590 | |
| | 1591 | /* ------------------------------------------------------------------------ */ |
| | 1592 | /* |
| | 1593 | * property evaluator - indexWhich |
| | 1594 | */ |
| | 1595 | int CVmObjList::getp_index_which(VMG_ vm_val_t *retval, |
| | 1596 | const vm_val_t *self_val, |
| | 1597 | const char *lst, uint *argc) |
| | 1598 | { |
| | 1599 | /* use the generic index-which routine, stepping forward */ |
| | 1600 | return gen_index_which(vmg_ retval, self_val, lst, argc, TRUE); |
| | 1601 | } |
| | 1602 | |
| | 1603 | /* |
| | 1604 | * general index finder for indexWhich and lastIndexWhich - steps either |
| | 1605 | * forward or backward through the list |
| | 1606 | */ |
| | 1607 | int CVmObjList::gen_index_which(VMG_ vm_val_t *retval, |
| | 1608 | const vm_val_t *self_val, |
| | 1609 | const char *lst, uint *argc, |
| | 1610 | int forward) |
| | 1611 | { |
| | 1612 | const vm_val_t *func_val; |
| | 1613 | size_t cnt; |
| | 1614 | size_t idx; |
| | 1615 | static CVmNativeCodeDesc desc(1); |
| | 1616 | |
| | 1617 | /* check arguments */ |
| | 1618 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1619 | return TRUE; |
| | 1620 | |
| | 1621 | /* get the function pointer argument, but leave it on the stack */ |
| | 1622 | func_val = G_stk->get(0); |
| | 1623 | |
| | 1624 | /* push a self-reference while allocating to protect from gc */ |
| | 1625 | G_stk->push(self_val); |
| | 1626 | |
| | 1627 | /* get the length of the list */ |
| | 1628 | cnt = vmb_get_len(lst); |
| | 1629 | |
| | 1630 | /* presume that we won't find any element that satisfies the condition */ |
| | 1631 | retval->set_nil(); |
| | 1632 | |
| | 1633 | /* |
| | 1634 | * start at either the first or last index, depending on which way |
| | 1635 | * we're stepping |
| | 1636 | */ |
| | 1637 | idx = (forward ? 1 : cnt); |
| | 1638 | |
| | 1639 | /* |
| | 1640 | * Go through each element of our list, and invoke the callback on |
| | 1641 | * the element. Stop when we reach the first element that returns |
| | 1642 | * true, or when we run out of elements. |
| | 1643 | */ |
| | 1644 | for (;;) |
| | 1645 | { |
| | 1646 | /* if we're out of elements, stop now */ |
| | 1647 | if (forward ? idx > cnt : idx == 0) |
| | 1648 | break; |
| | 1649 | |
| | 1650 | /* re-translate the list address in case of swapping */ |
| | 1651 | if (self_val->typ == VM_LIST) |
| | 1652 | lst = self_val->get_as_list(vmg0_); |
| | 1653 | |
| | 1654 | /* get this element, and push it as the callback's argument */ |
| | 1655 | index_and_push(vmg_ lst, idx); |
| | 1656 | |
| | 1657 | /* invoke the callback */ |
| | 1658 | G_interpreter->call_func_ptr(vmg_ func_val, 1, "list.indexWhich", 0); |
| | 1659 | |
| | 1660 | /* |
| | 1661 | * if the callback returned true, we've found the element we're |
| | 1662 | * looking for |
| | 1663 | */ |
| | 1664 | if (G_interpreter->get_r0()->typ == VM_NIL |
| | 1665 | || (G_interpreter->get_r0()->typ == VM_INT |
| | 1666 | && G_interpreter->get_r0()->val.intval == 0)) |
| | 1667 | { |
| | 1668 | /* nil or zero - this one failed the test, so keep looking */ |
| | 1669 | } |
| | 1670 | else |
| | 1671 | { |
| | 1672 | /* it passed the test - return its index */ |
| | 1673 | retval->set_int(idx); |
| | 1674 | |
| | 1675 | /* no need to keep searching - we found what we're looking for */ |
| | 1676 | break; |
| | 1677 | } |
| | 1678 | |
| | 1679 | /* advance to the next element */ |
| | 1680 | if (forward) |
| | 1681 | ++idx; |
| | 1682 | else |
| | 1683 | --idx; |
| | 1684 | } |
| | 1685 | |
| | 1686 | /* discard our gc protection (self) and our arguments */ |
| | 1687 | G_stk->discard(2); |
| | 1688 | |
| | 1689 | /* handled */ |
| | 1690 | return TRUE; |
| | 1691 | } |
| | 1692 | |
| | 1693 | /* ------------------------------------------------------------------------ */ |
| | 1694 | /* |
| | 1695 | * property evaluator - forEach |
| | 1696 | */ |
| | 1697 | int CVmObjList::getp_for_each(VMG_ vm_val_t *retval, |
| | 1698 | const vm_val_t *self_val, |
| | 1699 | const char *lst, uint *argc) |
| | 1700 | { |
| | 1701 | /* use the generic forEach/forEachAssoc processor */ |
| | 1702 | return for_each_gen(vmg_ retval, self_val, lst, argc, FALSE); |
| | 1703 | } |
| | 1704 | |
| | 1705 | /* |
| | 1706 | * property evaluator - forEachAssoc |
| | 1707 | */ |
| | 1708 | int CVmObjList::getp_for_each_assoc(VMG_ vm_val_t *retval, |
| | 1709 | const vm_val_t *self_val, |
| | 1710 | const char *lst, uint *argc) |
| | 1711 | { |
| | 1712 | /* use the generic forEach/forEachAssoc processor */ |
| | 1713 | return for_each_gen(vmg_ retval, self_val, lst, argc, TRUE); |
| | 1714 | } |
| | 1715 | |
| | 1716 | /* |
| | 1717 | * General forEach processor - combines the functionality of forEach and |
| | 1718 | * forEachAssoc, using a flag to specify whether or not to pass the index |
| | 1719 | * of each element to the callback. |
| | 1720 | */ |
| | 1721 | int CVmObjList::for_each_gen(VMG_ vm_val_t *retval, |
| | 1722 | const vm_val_t *self_val, |
| | 1723 | const char *lst, uint *argc, |
| | 1724 | int send_idx_to_cb) |
| | 1725 | { |
| | 1726 | const vm_val_t *func_val; |
| | 1727 | size_t cnt; |
| | 1728 | size_t idx; |
| | 1729 | static CVmNativeCodeDesc desc(1); |
| | 1730 | |
| | 1731 | /* check arguments */ |
| | 1732 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1733 | return TRUE; |
| | 1734 | |
| | 1735 | /* get the function pointer argument, but leave it on the stack */ |
| | 1736 | func_val = G_stk->get(0); |
| | 1737 | |
| | 1738 | /* push a self-reference while allocating to protect from gc */ |
| | 1739 | G_stk->push(self_val); |
| | 1740 | |
| | 1741 | /* get the length of the list */ |
| | 1742 | cnt = vmb_get_len(lst); |
| | 1743 | |
| | 1744 | /* no return value */ |
| | 1745 | retval->set_nil(); |
| | 1746 | |
| | 1747 | /* invoke the callback on each element */ |
| | 1748 | for (idx = 1 ; idx <= cnt ; ++idx) |
| | 1749 | { |
| | 1750 | /* re-translate the list address in case of swapping */ |
| | 1751 | if (self_val->typ == VM_LIST) |
| | 1752 | lst = self_val->get_as_list(vmg0_); |
| | 1753 | |
| | 1754 | /* |
| | 1755 | * get this element (using a 1-based index) and push it as the |
| | 1756 | * callback's argument |
| | 1757 | */ |
| | 1758 | index_and_push(vmg_ lst, idx); |
| | 1759 | |
| | 1760 | /* push the index, if desired */ |
| | 1761 | if (send_idx_to_cb) |
| | 1762 | G_stk->push()->set_int(idx); |
| | 1763 | |
| | 1764 | /* invoke the callback */ |
| | 1765 | G_interpreter->call_func_ptr(vmg_ func_val, send_idx_to_cb ? 2 : 1, |
| | 1766 | "list.forEach", 0); |
| | 1767 | } |
| | 1768 | |
| | 1769 | /* discard our gc protection (self) and our arguments */ |
| | 1770 | G_stk->discard(2); |
| | 1771 | |
| | 1772 | /* handled */ |
| | 1773 | return TRUE; |
| | 1774 | } |
| | 1775 | |
| | 1776 | /* ------------------------------------------------------------------------ */ |
| | 1777 | /* |
| | 1778 | * property evaluator - valWhich |
| | 1779 | */ |
| | 1780 | int CVmObjList::getp_val_which(VMG_ vm_val_t *retval, |
| | 1781 | const vm_val_t *self_val, |
| | 1782 | const char *lst, uint *argc) |
| | 1783 | { |
| | 1784 | /* get the index of the value using indexWhich */ |
| | 1785 | getp_index_which(vmg_ retval, self_val, lst, argc); |
| | 1786 | |
| | 1787 | /* if the return value is a valid index, get the value at the index */ |
| | 1788 | if (retval->typ == VM_INT) |
| | 1789 | { |
| | 1790 | int idx; |
| | 1791 | |
| | 1792 | /* re-translate the list address in case of swapping */ |
| | 1793 | if (self_val->typ == VM_LIST) |
| | 1794 | lst = self_val->get_as_list(vmg0_); |
| | 1795 | |
| | 1796 | /* get the element as the return value */ |
| | 1797 | idx = (int)retval->val.intval; |
| | 1798 | index_list(vmg_ retval, lst, idx); |
| | 1799 | } |
| | 1800 | |
| | 1801 | /* handled */ |
| | 1802 | return TRUE; |
| | 1803 | } |
| | 1804 | |
| | 1805 | /* ------------------------------------------------------------------------ */ |
| | 1806 | /* |
| | 1807 | * property evaluator - lastIndexOf |
| | 1808 | */ |
| | 1809 | int CVmObjList::getp_last_index_of(VMG_ vm_val_t *retval, |
| | 1810 | const vm_val_t *self_val, |
| | 1811 | const char *lst, uint *argc) |
| | 1812 | { |
| | 1813 | vm_val_t subval; |
| | 1814 | size_t idx; |
| | 1815 | static CVmNativeCodeDesc desc(1); |
| | 1816 | |
| | 1817 | /* check arguments */ |
| | 1818 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1819 | return TRUE; |
| | 1820 | |
| | 1821 | /* pop the value to find */ |
| | 1822 | G_stk->pop(&subval); |
| | 1823 | |
| | 1824 | /* find the value in the list */ |
| | 1825 | if (find_last_in_list(vmg_ self_val, &subval, &idx)) |
| | 1826 | { |
| | 1827 | /* found it - adjust to 1-based index for return */ |
| | 1828 | retval->set_int(idx + 1); |
| | 1829 | } |
| | 1830 | else |
| | 1831 | { |
| | 1832 | /* didn't find it - return nil */ |
| | 1833 | retval->set_nil(); |
| | 1834 | } |
| | 1835 | |
| | 1836 | /* handled */ |
| | 1837 | return TRUE; |
| | 1838 | } |
| | 1839 | |
| | 1840 | /* ------------------------------------------------------------------------ */ |
| | 1841 | /* |
| | 1842 | * property evaluator - lastIndexWhich |
| | 1843 | */ |
| | 1844 | int CVmObjList::getp_last_index_which(VMG_ vm_val_t *retval, |
| | 1845 | const vm_val_t *self_val, |
| | 1846 | const char *lst, uint *argc) |
| | 1847 | { |
| | 1848 | /* use the generic index-which routine, stepping backward */ |
| | 1849 | return gen_index_which(vmg_ retval, self_val, lst, argc, FALSE); |
| | 1850 | } |
| | 1851 | |
| | 1852 | /* ------------------------------------------------------------------------ */ |
| | 1853 | /* |
| | 1854 | * property evaluator - lastValWhich |
| | 1855 | */ |
| | 1856 | int CVmObjList::getp_last_val_which(VMG_ vm_val_t *retval, |
| | 1857 | const vm_val_t *self_val, |
| | 1858 | const char *lst, uint *argc) |
| | 1859 | { |
| | 1860 | /* get the index of the value using lastIndexWhich */ |
| | 1861 | getp_last_index_which(vmg_ retval, self_val, lst, argc); |
| | 1862 | |
| | 1863 | /* if the return value is a valid index, get the value at the index */ |
| | 1864 | if (retval->typ == VM_INT) |
| | 1865 | { |
| | 1866 | int idx; |
| | 1867 | |
| | 1868 | /* re-translate the list address in case of swapping */ |
| | 1869 | if (self_val->typ == VM_LIST) |
| | 1870 | lst = self_val->get_as_list(vmg0_); |
| | 1871 | |
| | 1872 | /* get the element as the return value */ |
| | 1873 | idx = (int)retval->val.intval; |
| | 1874 | index_list(vmg_ retval, lst, idx); |
| | 1875 | } |
| | 1876 | |
| | 1877 | /* handled */ |
| | 1878 | return TRUE; |
| | 1879 | } |
| | 1880 | |
| | 1881 | /* ------------------------------------------------------------------------ */ |
| | 1882 | /* |
| | 1883 | * property evaluator - countOf |
| | 1884 | */ |
| | 1885 | int CVmObjList::getp_count_of(VMG_ vm_val_t *retval, |
| | 1886 | const vm_val_t *self_val, |
| | 1887 | const char *lst, uint *argc) |
| | 1888 | { |
| | 1889 | vm_val_t *val; |
| | 1890 | size_t idx; |
| | 1891 | size_t cnt; |
| | 1892 | size_t val_cnt; |
| | 1893 | static CVmNativeCodeDesc desc(1); |
| | 1894 | |
| | 1895 | /* check arguments */ |
| | 1896 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1897 | return TRUE; |
| | 1898 | |
| | 1899 | /* get the value to find, but leave it on the stack for gc protection */ |
| | 1900 | val = G_stk->get(0); |
| | 1901 | |
| | 1902 | /* lave the self value on the stack for gc protection */ |
| | 1903 | G_stk->push(self_val); |
| | 1904 | |
| | 1905 | /* get the number of elements in the list */ |
| | 1906 | cnt = vmb_get_len(lst); |
| | 1907 | |
| | 1908 | /* scan the list and count the elements */ |
| | 1909 | for (idx = 0, val_cnt = 0 ; idx < cnt ; ++idx) |
| | 1910 | { |
| | 1911 | vm_val_t ele; |
| | 1912 | |
| | 1913 | /* re-translate the list address in case of swapping */ |
| | 1914 | if (self_val->typ == VM_LIST) |
| | 1915 | lst = self_val->get_as_list(vmg0_); |
| | 1916 | |
| | 1917 | /* get this list element */ |
| | 1918 | vmb_get_dh(get_element_ptr_const(lst, idx), &ele); |
| | 1919 | |
| | 1920 | /* if it's the one we're looking for, count it */ |
| | 1921 | if (ele.equals(vmg_ val)) |
| | 1922 | ++val_cnt; |
| | 1923 | } |
| | 1924 | |
| | 1925 | /* discard our gc protection */ |
| | 1926 | G_stk->discard(2); |
| | 1927 | |
| | 1928 | /* return the count */ |
| | 1929 | retval->set_int(val_cnt); |
| | 1930 | |
| | 1931 | /* handled */ |
| | 1932 | return TRUE; |
| | 1933 | } |
| | 1934 | |
| | 1935 | /* ------------------------------------------------------------------------ */ |
| | 1936 | /* |
| | 1937 | * property evaluator - countWhich |
| | 1938 | */ |
| | 1939 | int CVmObjList::getp_count_which(VMG_ vm_val_t *retval, |
| | 1940 | const vm_val_t *self_val, |
| | 1941 | const char *lst, uint *argc) |
| | 1942 | { |
| | 1943 | const vm_val_t *func_val; |
| | 1944 | size_t cnt; |
| | 1945 | size_t idx; |
| | 1946 | int val_cnt; |
| | 1947 | static CVmNativeCodeDesc desc(1); |
| | 1948 | |
| | 1949 | /* check arguments */ |
| | 1950 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1951 | return TRUE; |
| | 1952 | |
| | 1953 | /* get the function pointer argument, but leave it on the stack */ |
| | 1954 | func_val = G_stk->get(0); |
| | 1955 | |
| | 1956 | /* push a self-reference while allocating to protect from gc */ |
| | 1957 | G_stk->push(self_val); |
| | 1958 | |
| | 1959 | /* get the length of the list */ |
| | 1960 | cnt = vmb_get_len(lst); |
| | 1961 | |
| | 1962 | /* no return value */ |
| | 1963 | retval->set_nil(); |
| | 1964 | |
| | 1965 | /* invoke the callback on each element */ |
| | 1966 | for (idx = 1, val_cnt = 0 ; idx <= cnt ; ++idx) |
| | 1967 | { |
| | 1968 | vm_val_t *val; |
| | 1969 | |
| | 1970 | /* re-translate the list address in case of swapping */ |
| | 1971 | if (self_val->typ == VM_LIST) |
| | 1972 | lst = self_val->get_as_list(vmg0_); |
| | 1973 | |
| | 1974 | /* |
| | 1975 | * get this element (using a 1-based index), and push it as the |
| | 1976 | * callback's argument |
| | 1977 | */ |
| | 1978 | index_and_push(vmg_ lst, idx); |
| | 1979 | |
| | 1980 | /* invoke the callback */ |
| | 1981 | G_interpreter->call_func_ptr(vmg_ func_val, 1, "list.forEach", 0); |
| | 1982 | |
| | 1983 | /* get the result from R0 */ |
| | 1984 | val = G_interpreter->get_r0(); |
| | 1985 | |
| | 1986 | /* if the callback returned non-nil and non-zero, count it */ |
| | 1987 | if (val->typ == VM_NIL |
| | 1988 | || (val->typ == VM_INT && val->val.intval == 0)) |
| | 1989 | { |
| | 1990 | /* it's nil or zero - don't include it in the result */ |
| | 1991 | } |
| | 1992 | else |
| | 1993 | { |
| | 1994 | /* count it */ |
| | 1995 | ++val_cnt; |
| | 1996 | } |
| | 1997 | } |
| | 1998 | |
| | 1999 | /* discard our gc protection (self) and our arguments */ |
| | 2000 | G_stk->discard(2); |
| | 2001 | |
| | 2002 | /* return the count */ |
| | 2003 | retval->set_int(val_cnt); |
| | 2004 | |
| | 2005 | /* handled */ |
| | 2006 | return TRUE; |
| | 2007 | } |
| | 2008 | |
| | 2009 | /* ------------------------------------------------------------------------ */ |
| | 2010 | /* |
| | 2011 | * property evaluator - getUnique |
| | 2012 | */ |
| | 2013 | int CVmObjList::getp_get_unique(VMG_ vm_val_t *retval, |
| | 2014 | const vm_val_t *self_val, |
| | 2015 | const char *lst, uint *argc) |
| | 2016 | { |
| | 2017 | CVmObjList *new_lst_obj; |
| | 2018 | static CVmNativeCodeDesc desc(0); |
| | 2019 | |
| | 2020 | /* check arguments */ |
| | 2021 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2022 | return TRUE; |
| | 2023 | |
| | 2024 | /* put myself on the stack for GC protection */ |
| | 2025 | G_stk->push(self_val); |
| | 2026 | |
| | 2027 | /* |
| | 2028 | * Make a copy of our list for the return value, since the result |
| | 2029 | * value will never be larger than the original list. By making a |
| | 2030 | * copy of the input list, we also can avoid worrying about whether |
| | 2031 | * the input list was a constant, and hence we don't have to worry |
| | 2032 | * about the possibility of constant page swapping - we'll just |
| | 2033 | * update elements of the copy in-place. |
| | 2034 | */ |
| | 2035 | retval->set_obj(create(vmg_ FALSE, lst)); |
| | 2036 | |
| | 2037 | /* get the return value list data */ |
| | 2038 | new_lst_obj = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 2039 | |
| | 2040 | /* push a reference to the new list for gc protection */ |
| | 2041 | G_stk->push(retval); |
| | 2042 | |
| | 2043 | /* uniquify the list */ |
| | 2044 | new_lst_obj->cons_uniquify(vmg0_); |
| | 2045 | |
| | 2046 | /* discard the gc protection */ |
| | 2047 | G_stk->discard(2); |
| | 2048 | |
| | 2049 | /* handled */ |
| | 2050 | return TRUE; |
| | 2051 | } |
| | 2052 | |
| | 2053 | /* ------------------------------------------------------------------------ */ |
| | 2054 | /* |
| | 2055 | * property evaluator - appendUnique |
| | 2056 | */ |
| | 2057 | int CVmObjList::getp_append_unique(VMG_ vm_val_t *retval, |
| | 2058 | const vm_val_t *self_val, |
| | 2059 | const char *lst, uint *argc) |
| | 2060 | { |
| | 2061 | vm_val_t val2; |
| | 2062 | const char *lst2; |
| | 2063 | size_t lst_len; |
| | 2064 | size_t lst2_len; |
| | 2065 | CVmObjList *new_lst; |
| | 2066 | static CVmNativeCodeDesc desc(1); |
| | 2067 | |
| | 2068 | /* check arguments */ |
| | 2069 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2070 | return TRUE; |
| | 2071 | |
| | 2072 | /* remember the length of my list */ |
| | 2073 | lst_len = vmb_get_len(lst); |
| | 2074 | |
| | 2075 | /* get the second list, but leave it on the stack for GC protection */ |
| | 2076 | val2 = *G_stk->get(0); |
| | 2077 | |
| | 2078 | /* put myself on the stack for GC protection as well */ |
| | 2079 | G_stk->push(self_val); |
| | 2080 | |
| | 2081 | /* make sure the other value is indeed a list */ |
| | 2082 | if ((lst2 = val2.get_as_list(vmg0_)) == 0) |
| | 2083 | err_throw(VMERR_LIST_VAL_REQD); |
| | 2084 | |
| | 2085 | /* get the length of the second list */ |
| | 2086 | lst2_len = vmb_get_len(lst2); |
| | 2087 | |
| | 2088 | /* |
| | 2089 | * Create a new list for the return value. Allocate space for the |
| | 2090 | * current list plus the list to be added - this is an upper bound, |
| | 2091 | * since the actual result list can be shorter |
| | 2092 | */ |
| | 2093 | retval->set_obj(create(vmg_ FALSE, lst_len + lst2_len)); |
| | 2094 | |
| | 2095 | /* push a reference to the new list for gc protection */ |
| | 2096 | G_stk->push(retval); |
| | 2097 | |
| | 2098 | /* get the return value list data */ |
| | 2099 | new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 2100 | |
| | 2101 | /* |
| | 2102 | * copy the first list into the result list (including only the data |
| | 2103 | * elements, not the length prefix) |
| | 2104 | */ |
| | 2105 | lst = self_val->get_as_list(vmg0_); |
| | 2106 | new_lst->cons_copy_elements(0, lst); |
| | 2107 | |
| | 2108 | /* append the second list to the result list */ |
| | 2109 | lst2 = val2.get_as_list(vmg0_); |
| | 2110 | new_lst->cons_copy_elements(lst_len, lst2); |
| | 2111 | |
| | 2112 | /* make the list unique */ |
| | 2113 | new_lst->cons_uniquify(vmg0_); |
| | 2114 | |
| | 2115 | /* discard the gc protection and arguments */ |
| | 2116 | G_stk->discard(3); |
| | 2117 | |
| | 2118 | /* handled */ |
| | 2119 | return TRUE; |
| | 2120 | } |
| | 2121 | |
| | 2122 | |
| | 2123 | /* ------------------------------------------------------------------------ */ |
| | 2124 | /* |
| | 2125 | * General insertion routine - this is used to handle append, prepend, and |
| | 2126 | * insertAt property evaluators. Inserts elements from the argument list |
| | 2127 | * at the given index, with zero indicating insertion before the first |
| | 2128 | * existing element. |
| | 2129 | */ |
| | 2130 | void CVmObjList::insert_elements(VMG_ vm_val_t *retval, |
| | 2131 | const vm_val_t *self_val, |
| | 2132 | const char *lst, uint argc, int idx) |
| | 2133 | { |
| | 2134 | size_t lst_len; |
| | 2135 | CVmObjList *new_lst; |
| | 2136 | uint i; |
| | 2137 | const int stack_temp_cnt = 2; |
| | 2138 | |
| | 2139 | /* remember the length of my list */ |
| | 2140 | lst_len = vmb_get_len(lst); |
| | 2141 | |
| | 2142 | /* the index must be in the range 0 to the number of elements */ |
| | 2143 | if (idx < 0 || (size_t)idx > lst_len) |
| | 2144 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 2145 | |
| | 2146 | /* put myself on the stack for GC protection as well */ |
| | 2147 | G_stk->push(self_val); |
| | 2148 | |
| | 2149 | /* |
| | 2150 | * Create a new list for the return value. Allocate space for the |
| | 2151 | * current list plus one new element for each argument. |
| | 2152 | */ |
| | 2153 | retval->set_obj(create(vmg_ FALSE, lst_len + argc)); |
| | 2154 | |
| | 2155 | /* push a reference to the new list for gc protection */ |
| | 2156 | G_stk->push(retval); |
| | 2157 | |
| | 2158 | /* get the return value list data */ |
| | 2159 | new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 2160 | |
| | 2161 | /* get the original list data */ |
| | 2162 | lst = self_val->get_as_list(vmg0_); |
| | 2163 | |
| | 2164 | /* |
| | 2165 | * Copy the first list into the result list (including only the data |
| | 2166 | * elements, not the length prefix). Copy it in two pieces: first, |
| | 2167 | * copy the elements before the insertion point. |
| | 2168 | */ |
| | 2169 | if (idx != 0) |
| | 2170 | new_lst->cons_copy_data(0, get_element_ptr_const(lst, 0), idx); |
| | 2171 | |
| | 2172 | /* second, copy the elements after the insertion point */ |
| | 2173 | if ((size_t)idx != lst_len) |
| | 2174 | new_lst->cons_copy_data(idx + argc, get_element_ptr_const(lst, idx), |
| | 2175 | lst_len - idx); |
| | 2176 | |
| | 2177 | /* copy each argument into the proper position in the new list */ |
| | 2178 | for (i = 0 ; i < argc ; ++i) |
| | 2179 | { |
| | 2180 | const vm_val_t *argp; |
| | 2181 | |
| | 2182 | /* |
| | 2183 | * get a pointer to this argument value - the arguments are just |
| | 2184 | * after the temporary items we've pushed onto the stack |
| | 2185 | */ |
| | 2186 | argp = G_stk->get(stack_temp_cnt + i); |
| | 2187 | |
| | 2188 | /* copy the argument into the list */ |
| | 2189 | new_lst->cons_set_element((uint)idx + i, argp); |
| | 2190 | } |
| | 2191 | |
| | 2192 | /* discard the gc protection and arguments */ |
| | 2193 | G_stk->discard(argc + stack_temp_cnt); |
| | 2194 | } |
| | 2195 | |
| | 2196 | |
| | 2197 | /* ------------------------------------------------------------------------ */ |
| | 2198 | /* |
| | 2199 | * property evaluator - append |
| | 2200 | */ |
| | 2201 | int CVmObjList::getp_append(VMG_ vm_val_t *retval, |
| | 2202 | const vm_val_t *self_val, |
| | 2203 | const char *lst, uint *argc) |
| | 2204 | { |
| | 2205 | static CVmNativeCodeDesc desc(1); |
| | 2206 | |
| | 2207 | /* check arguments */ |
| | 2208 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2209 | return TRUE; |
| | 2210 | |
| | 2211 | /* insert the element (there's just one) at the end of the list */ |
| | 2212 | insert_elements(vmg_ retval, self_val, lst, 1, vmb_get_len(lst)); |
| | 2213 | |
| | 2214 | /* handled */ |
| | 2215 | return TRUE; |
| | 2216 | } |
| | 2217 | |
| | 2218 | /* ------------------------------------------------------------------------ */ |
| | 2219 | /* |
| | 2220 | * property evaluator - prepend |
| | 2221 | */ |
| | 2222 | int CVmObjList::getp_prepend(VMG_ vm_val_t *retval, |
| | 2223 | const vm_val_t *self_val, |
| | 2224 | const char *lst, uint *argc) |
| | 2225 | { |
| | 2226 | static CVmNativeCodeDesc desc(1); |
| | 2227 | |
| | 2228 | /* check arguments */ |
| | 2229 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2230 | return TRUE; |
| | 2231 | |
| | 2232 | /* insert the element (there's just one) at the start of the list */ |
| | 2233 | insert_elements(vmg_ retval, self_val, lst, 1, 0); |
| | 2234 | |
| | 2235 | /* handled */ |
| | 2236 | return TRUE; |
| | 2237 | } |
| | 2238 | |
| | 2239 | /* ------------------------------------------------------------------------ */ |
| | 2240 | /* |
| | 2241 | * property evaluator - insert new elements |
| | 2242 | */ |
| | 2243 | int CVmObjList::getp_insert_at(VMG_ vm_val_t *retval, |
| | 2244 | const vm_val_t *self_val, |
| | 2245 | const char *lst, uint *in_argc) |
| | 2246 | { |
| | 2247 | int idx; |
| | 2248 | uint argc = (in_argc != 0 ? *in_argc : 0); |
| | 2249 | static CVmNativeCodeDesc desc(2, 0, TRUE); |
| | 2250 | |
| | 2251 | /* check arguments - we need at least two */ |
| | 2252 | if (get_prop_check_argc(retval, in_argc, &desc)) |
| | 2253 | return TRUE; |
| | 2254 | |
| | 2255 | /* |
| | 2256 | * Pop the index value - the remaining arguments are the new element |
| | 2257 | * values to be inserted, so leave them on the stack. Note that we |
| | 2258 | * must adjust the value from the 1-based indexing of our caller to |
| | 2259 | * the zero-based indexing we use internally. |
| | 2260 | */ |
| | 2261 | idx = CVmBif::pop_int_val(vmg0_) - 1; |
| | 2262 | |
| | 2263 | /* |
| | 2264 | * Insert the element (there's just one) at the start of the list. |
| | 2265 | * Note that we must decrement the argument count we got, since we |
| | 2266 | * already took off the first argument (the index value). |
| | 2267 | */ |
| | 2268 | insert_elements(vmg_ retval, self_val, lst, argc - 1, idx); |
| | 2269 | |
| | 2270 | /* handled */ |
| | 2271 | return TRUE; |
| | 2272 | } |
| | 2273 | |
| | 2274 | /* ------------------------------------------------------------------------ */ |
| | 2275 | /* |
| | 2276 | * General property evaluator for removing a range of elements - this |
| | 2277 | * handles removeElementAt and removeRange. |
| | 2278 | */ |
| | 2279 | void CVmObjList::remove_range(VMG_ vm_val_t *retval, |
| | 2280 | const vm_val_t *self_val, |
| | 2281 | const char *lst, int start_idx, int del_cnt) |
| | 2282 | { |
| | 2283 | size_t lst_len; |
| | 2284 | CVmObjList *new_lst; |
| | 2285 | |
| | 2286 | /* push myself onto the stack for GC protection */ |
| | 2287 | G_stk->push(self_val); |
| | 2288 | |
| | 2289 | /* get the original list length */ |
| | 2290 | lst_len = vmb_get_len(lst); |
| | 2291 | |
| | 2292 | /* |
| | 2293 | * allocate a new list with space for the original list minus the |
| | 2294 | * elements to be deleted |
| | 2295 | */ |
| | 2296 | retval->set_obj(create(vmg_ FALSE, lst_len - del_cnt)); |
| | 2297 | |
| | 2298 | /* push a reference to teh new list for gc protection */ |
| | 2299 | G_stk->push(retval); |
| | 2300 | |
| | 2301 | /* get the return value list data */ |
| | 2302 | new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 2303 | |
| | 2304 | /* get the original list data as well */ |
| | 2305 | lst = self_val->get_as_list(vmg0_); |
| | 2306 | |
| | 2307 | /* |
| | 2308 | * copy elements from the original list up to the first item to be |
| | 2309 | * removed |
| | 2310 | */ |
| | 2311 | if (start_idx != 0) |
| | 2312 | new_lst->cons_copy_data(0, get_element_ptr_const(lst, 0), start_idx); |
| | 2313 | |
| | 2314 | /* |
| | 2315 | * copy elements of the original list following the last item to be |
| | 2316 | * removed |
| | 2317 | */ |
| | 2318 | if ((size_t)(start_idx + del_cnt) < lst_len) |
| | 2319 | new_lst-> |
| | 2320 | cons_copy_data(start_idx, |
| | 2321 | get_element_ptr_const(lst, start_idx + del_cnt), |
| | 2322 | lst_len - (start_idx + del_cnt)); |
| | 2323 | |
| | 2324 | /* discard the gc protection */ |
| | 2325 | G_stk->discard(2); |
| | 2326 | } |
| | 2327 | |
| | 2328 | /* ------------------------------------------------------------------------ */ |
| | 2329 | /* |
| | 2330 | * property evaluator - remove the element at the given index |
| | 2331 | */ |
| | 2332 | int CVmObjList::getp_remove_element_at(VMG_ vm_val_t *retval, |
| | 2333 | const vm_val_t *self_val, |
| | 2334 | const char *lst, uint *argc) |
| | 2335 | { |
| | 2336 | int idx; |
| | 2337 | static CVmNativeCodeDesc desc(1); |
| | 2338 | |
| | 2339 | /* check arguments */ |
| | 2340 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2341 | return TRUE; |
| | 2342 | |
| | 2343 | /* retrieve the index value, and adjust to zero-based indexing */ |
| | 2344 | idx = CVmBif::pop_int_val(vmg0_) - 1; |
| | 2345 | |
| | 2346 | /* make sure it's in range - it must refer to a valid element */ |
| | 2347 | if (idx < 0 || (size_t)idx >= vmb_get_len(lst)) |
| | 2348 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 2349 | |
| | 2350 | /* remove one element at the given index */ |
| | 2351 | remove_range(vmg_ retval, self_val, lst, idx, 1); |
| | 2352 | |
| | 2353 | /* handled */ |
| | 2354 | return TRUE; |
| | 2355 | } |
| | 2356 | |
| | 2357 | /* ------------------------------------------------------------------------ */ |
| | 2358 | /* |
| | 2359 | * property evaluator - remove the element at the given index |
| | 2360 | */ |
| | 2361 | int CVmObjList::getp_remove_range(VMG_ vm_val_t *retval, |
| | 2362 | const vm_val_t *self_val, |
| | 2363 | const char *lst, uint *argc) |
| | 2364 | { |
| | 2365 | int start_idx; |
| | 2366 | int end_idx; |
| | 2367 | static CVmNativeCodeDesc desc(2); |
| | 2368 | |
| | 2369 | /* check arguments */ |
| | 2370 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2371 | return TRUE; |
| | 2372 | |
| | 2373 | /* |
| | 2374 | * retrieve the starting and ending index values, and adjust to |
| | 2375 | * zero-based indexing |
| | 2376 | */ |
| | 2377 | start_idx = CVmBif::pop_int_val(vmg0_) - 1; |
| | 2378 | end_idx = CVmBif::pop_int_val(vmg0_) - 1; |
| | 2379 | |
| | 2380 | /* |
| | 2381 | * make sure the index values are in range - both must refer to valid |
| | 2382 | * elements, and the ending index must be at least as high as the |
| | 2383 | * starting index |
| | 2384 | */ |
| | 2385 | if (start_idx < 0 || (size_t)start_idx >= vmb_get_len(lst) |
| | 2386 | || end_idx < 0 || (size_t)end_idx >= vmb_get_len(lst) |
| | 2387 | || end_idx < start_idx) |
| | 2388 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 2389 | |
| | 2390 | /* remove the specified elements */ |
| | 2391 | remove_range(vmg_ retval, self_val, lst, start_idx, |
| | 2392 | end_idx - start_idx + 1); |
| | 2393 | |
| | 2394 | /* handled */ |
| | 2395 | return TRUE; |
| | 2396 | } |
| | 2397 | |
| | 2398 | /* ------------------------------------------------------------------------ */ |
| | 2399 | /* |
| | 2400 | * sorter for list data |
| | 2401 | */ |
| | 2402 | class CVmQSortList: public CVmQSortVal |
| | 2403 | { |
| | 2404 | public: |
| | 2405 | CVmQSortList() |
| | 2406 | { |
| | 2407 | lst_ = 0; |
| | 2408 | } |
| | 2409 | |
| | 2410 | /* get an element */ |
| | 2411 | void get_ele(VMG_ size_t idx, vm_val_t *val) |
| | 2412 | { |
| | 2413 | vmb_get_dh(get_ele_ptr(idx), val); |
| | 2414 | } |
| | 2415 | |
| | 2416 | /* set an element */ |
| | 2417 | void set_ele(VMG_ size_t idx, const vm_val_t *val) |
| | 2418 | { |
| | 2419 | vmb_put_dh(get_ele_ptr(idx), val); |
| | 2420 | } |
| | 2421 | |
| | 2422 | /* get an element pointer */ |
| | 2423 | char *get_ele_ptr(size_t idx) |
| | 2424 | { return lst_ + VMB_LEN + (idx * VMB_DATAHOLDER); } |
| | 2425 | |
| | 2426 | /* our list data */ |
| | 2427 | char *lst_; |
| | 2428 | }; |
| | 2429 | |
| | 2430 | /* |
| | 2431 | * property evaluator - sort |
| | 2432 | */ |
| | 2433 | int CVmObjList::getp_sort(VMG_ vm_val_t *retval, |
| | 2434 | const vm_val_t *self_val, |
| | 2435 | const char *lst, uint *in_argc) |
| | 2436 | { |
| | 2437 | size_t lst_len; |
| | 2438 | CVmObjList *new_lst; |
| | 2439 | uint argc = (in_argc == 0 ? 0 : *in_argc); |
| | 2440 | CVmQSortList sorter; |
| | 2441 | static CVmNativeCodeDesc desc(0, 2); |
| | 2442 | |
| | 2443 | /* check arguments */ |
| | 2444 | if (get_prop_check_argc(retval, in_argc, &desc)) |
| | 2445 | return TRUE; |
| | 2446 | |
| | 2447 | /* remember the length of my list */ |
| | 2448 | lst_len = vmb_get_len(lst); |
| | 2449 | |
| | 2450 | /* if we have an 'descending' flag, note it */ |
| | 2451 | if (argc >= 1) |
| | 2452 | sorter.descending_ = (G_stk->get(0)->typ != VM_NIL); |
| | 2453 | |
| | 2454 | /* |
| | 2455 | * if we have a comparison function, note it, but leave it on the |
| | 2456 | * stack for gc protection |
| | 2457 | */ |
| | 2458 | if (argc >= 2) |
| | 2459 | sorter.compare_fn_ = *G_stk->get(1); |
| | 2460 | |
| | 2461 | /* put myself on the stack for GC protection as well */ |
| | 2462 | G_stk->push(self_val); |
| | 2463 | |
| | 2464 | /* create a copy of the list as the return value */ |
| | 2465 | retval->set_obj(create(vmg_ FALSE, lst)); |
| | 2466 | |
| | 2467 | /* push a reference to the new list for gc protection */ |
| | 2468 | G_stk->push(retval); |
| | 2469 | |
| | 2470 | /* get the return value list data */ |
| | 2471 | new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 2472 | |
| | 2473 | /* set the list pointer in the sorter */ |
| | 2474 | sorter.lst_ = new_lst->ext_; |
| | 2475 | |
| | 2476 | /* sort the new list if it has any elements */ |
| | 2477 | if (lst_len != 0) |
| | 2478 | sorter.sort(vmg_ 0, lst_len - 1); |
| | 2479 | |
| | 2480 | /* discard the gc protection and arguments */ |
| | 2481 | G_stk->discard(2 + argc); |
| | 2482 | |
| | 2483 | /* handled */ |
| | 2484 | return TRUE; |
| | 2485 | } |
| | 2486 | |
| | 2487 | |
| | 2488 | /* ------------------------------------------------------------------------ */ |
| | 2489 | /* |
| | 2490 | * Constant-pool list object |
| | 2491 | */ |
| | 2492 | |
| | 2493 | /* |
| | 2494 | * create |
| | 2495 | */ |
| | 2496 | vm_obj_id_t CVmObjListConst::create(VMG_ const char *const_ptr) |
| | 2497 | { |
| | 2498 | /* create our new ID */ |
| | 2499 | vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); |
| | 2500 | |
| | 2501 | /* create our list object, pointing directly to the constant pool */ |
| | 2502 | new (vmg_ id) CVmObjListConst(vmg_ const_ptr); |
| | 2503 | |
| | 2504 | /* return the new ID */ |
| | 2505 | return id; |
| | 2506 | } |