|
7 | 7 | #include "mpconfig.h" |
8 | 8 | #include "qstr.h" |
9 | 9 | #include "obj.h" |
| 10 | +#include "objtuple.h" |
10 | 11 | #include "objmodule.h" |
11 | 12 | #include "parsenum.h" |
12 | 13 | #include "runtime0.h" |
@@ -503,6 +504,148 @@ mp_obj_t mp_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args) { |
503 | 504 | return mp_call_function_n_kw(args[0], n_args + adjust, n_kw, args + 2 - adjust); |
504 | 505 | } |
505 | 506 |
|
| 507 | +mp_obj_t mp_call_method_n_kw_var(bool have_self, uint n_args_n_kw, const mp_obj_t *args, mp_obj_t pos_seq, mp_obj_t kw_dict) { |
| 508 | + mp_obj_t fun = *args++; |
| 509 | + mp_obj_t self = MP_OBJ_NULL; |
| 510 | + if (have_self) { |
| 511 | + self = *args++; // may be MP_OBJ_NULL |
| 512 | + } |
| 513 | + uint n_args = n_args_n_kw & 0xff; |
| 514 | + uint n_kw = (n_args_n_kw >> 8) & 0xff; |
| 515 | + |
| 516 | + DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, seq=%p, dict=%p)\n", fun, self, n_args, n_kw, args, pos_seq, kw_dict); |
| 517 | + |
| 518 | + // We need to create the following array of objects: |
| 519 | + // args[0 .. n_args] unpacked(pos_seq) args[n_args .. n_args + 2 * n_kw] unpacked(kw_dict) |
| 520 | + // TODO: optimize one day to avoid constructing new arg array? Will be hard. |
| 521 | + |
| 522 | + // The new args array |
| 523 | + mp_obj_t *args2; |
| 524 | + uint args2_alloc; |
| 525 | + uint args2_len = 0; |
| 526 | + |
| 527 | + // Try to get a hint for the size of the kw_dict |
| 528 | + uint kw_dict_len = 0; |
| 529 | + if (kw_dict != MP_OBJ_NULL && MP_OBJ_IS_TYPE(kw_dict, &mp_type_dict)) { |
| 530 | + kw_dict_len = mp_obj_dict_len(kw_dict); |
| 531 | + } |
| 532 | + |
| 533 | + // Extract the pos_seq sequence to the new args array. |
| 534 | + // Note that it can be arbitrary iterator. |
| 535 | + if (pos_seq == MP_OBJ_NULL) { |
| 536 | + // no sequence |
| 537 | + |
| 538 | + // allocate memory for the new array of args |
| 539 | + args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len); |
| 540 | + args2 = m_new(mp_obj_t, args2_alloc); |
| 541 | + |
| 542 | + // copy the self |
| 543 | + if (self != MP_OBJ_NULL) { |
| 544 | + args2[args2_len++] = self; |
| 545 | + } |
| 546 | + |
| 547 | + // copy the fixed pos args |
| 548 | + m_seq_copy(args2 + args2_len, args, n_args, mp_obj_t); |
| 549 | + args2_len += n_args; |
| 550 | + |
| 551 | + } else if (MP_OBJ_IS_TYPE(pos_seq, &mp_type_tuple) || MP_OBJ_IS_TYPE(pos_seq, &mp_type_list)) { |
| 552 | + // optimise the case of a tuple and list |
| 553 | + |
| 554 | + // get the items |
| 555 | + uint len; |
| 556 | + mp_obj_t *items; |
| 557 | + mp_obj_get_array(pos_seq, &len, &items); |
| 558 | + |
| 559 | + // allocate memory for the new array of args |
| 560 | + args2_alloc = 1 + n_args + len + 2 * (n_kw + kw_dict_len); |
| 561 | + args2 = m_new(mp_obj_t, args2_alloc); |
| 562 | + |
| 563 | + // copy the self |
| 564 | + if (self != MP_OBJ_NULL) { |
| 565 | + args2[args2_len++] = self; |
| 566 | + } |
| 567 | + |
| 568 | + // copy the fixed and variable position args |
| 569 | + m_seq_cat(args2 + args2_len, args, n_args, items, len, mp_obj_t); |
| 570 | + args2_len += n_args + len; |
| 571 | + |
| 572 | + } else { |
| 573 | + // generic iterator |
| 574 | + |
| 575 | + // allocate memory for the new array of args |
| 576 | + args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len) + 3; |
| 577 | + args2 = m_new(mp_obj_t, args2_alloc); |
| 578 | + |
| 579 | + // copy the self |
| 580 | + if (self != MP_OBJ_NULL) { |
| 581 | + args2[args2_len++] = self; |
| 582 | + } |
| 583 | + |
| 584 | + // copy the fixed position args |
| 585 | + m_seq_copy(args2 + args2_len, args, n_args, mp_obj_t); |
| 586 | + |
| 587 | + // extract the variable position args from the iterator |
| 588 | + mp_obj_t iterable = mp_getiter(pos_seq); |
| 589 | + mp_obj_t item; |
| 590 | + while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) { |
| 591 | + if (args2_len >= args2_alloc) { |
| 592 | + args2 = m_renew(mp_obj_t, args2, args2_alloc, args2_alloc * 2); |
| 593 | + args2_alloc *= 2; |
| 594 | + } |
| 595 | + args2[args2_len++] = item; |
| 596 | + } |
| 597 | + } |
| 598 | + |
| 599 | + // The size of the args2 array now is the number of positional args. |
| 600 | + uint pos_args_len = args2_len; |
| 601 | + |
| 602 | + // Copy the fixed kw args. |
| 603 | + m_seq_copy(args2 + args2_len, args + n_args, 2 * n_kw, mp_obj_t); |
| 604 | + args2_len += 2 * n_kw; |
| 605 | + |
| 606 | + // Extract (key,value) pairs from kw_dict dictionary and append to args2. |
| 607 | + // Note that it can be arbitrary iterator. |
| 608 | + if (kw_dict == MP_OBJ_NULL) { |
| 609 | + // pass |
| 610 | + } else if (MP_OBJ_IS_TYPE(kw_dict, &mp_type_dict)) { |
| 611 | + // dictionary |
| 612 | + mp_map_t *map = mp_obj_dict_get_map(kw_dict); |
| 613 | + assert(args2_len + 2 * map->used <= args2_alloc); // should have enough, since kw_dict_len is in this case hinted correctly above |
| 614 | + for (uint i = 0; i < map->alloc; i++) { |
| 615 | + if (map->table[i].key != MP_OBJ_NULL) { |
| 616 | + args2[args2_len++] = map->table[i].key; |
| 617 | + args2[args2_len++] = map->table[i].value; |
| 618 | + } |
| 619 | + } |
| 620 | + } else { |
| 621 | + // generic mapping |
| 622 | + // TODO is calling 'items' on the mapping the correct thing to do here? |
| 623 | + mp_obj_t dest[2]; |
| 624 | + mp_load_method(kw_dict, MP_QSTR_items, dest); |
| 625 | + mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest)); |
| 626 | + mp_obj_t item; |
| 627 | + while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) { |
| 628 | + if (args2_len + 1 >= args2_alloc) { |
| 629 | + uint new_alloc = args2_alloc * 2; |
| 630 | + if (new_alloc < 4) { |
| 631 | + new_alloc = 4; |
| 632 | + } |
| 633 | + args2 = m_renew(mp_obj_t, args2, args2_alloc, new_alloc); |
| 634 | + args2_alloc = new_alloc; |
| 635 | + } |
| 636 | + mp_obj_t *items; |
| 637 | + mp_obj_get_array_fixed_n(item, 2, &items); |
| 638 | + args2[args2_len++] = items[0]; |
| 639 | + args2[args2_len++] = items[1]; |
| 640 | + } |
| 641 | + } |
| 642 | + |
| 643 | + mp_obj_t res = mp_call_function_n_kw(fun, pos_args_len, (args2_len - pos_args_len) / 2, args2); |
| 644 | + m_del(mp_obj_t, args2, args2_alloc); |
| 645 | + |
| 646 | + return res; |
| 647 | +} |
| 648 | + |
506 | 649 | mp_obj_t mp_build_tuple(int n_args, mp_obj_t *items) { |
507 | 650 | return mp_obj_new_tuple(n_args, items); |
508 | 651 | } |
|
0 commit comments