Skip to content

Commit d474475

Browse files
authored
Merge pull request #7732 from saddamr3e/codegen-hash-update-before-limit
mruby-compiler: add pairs after a ** to the hash gen_hash started
2 parents 7edb4d1 + 2e9499e commit d474475

2 files changed

Lines changed: 69 additions & 6 deletions

File tree

‎mrbgems/mruby-compiler/src/codegen.c‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2606,12 +2606,10 @@ gen_hash(mrc_codegen_scope *s, mrc_node *tree, int val, int limit)
26062606
}
26072607
first = FALSE;
26082608
}
2609-
if (val && len > limit) {
2610-
pop_n(len*2);
2611-
genop_2(s, OP_HASH, cursp(), len);
2612-
push();
2613-
return -1;
2614-
}
2609+
/* Once a `**` or a flush has started the hash, the pairs left over are
2610+
added to it whatever their count, as gen_values_upto() tests `first`
2611+
before its limit: a hash of their own would sit a register above the
2612+
one the caller reads. */
26152613
if (update) {
26162614
if (val && len > 0) {
26172615
pop_n(len*2+1);
@@ -2620,6 +2618,12 @@ gen_hash(mrc_codegen_scope *s, mrc_node *tree, int val, int limit)
26202618
}
26212619
return -1; /* variable length */
26222620
}
2621+
if (val && len > limit) {
2622+
pop_n(len*2);
2623+
genop_2(s, OP_HASH, cursp(), len);
2624+
push();
2625+
return -1;
2626+
}
26232627
return len;
26242628
}
26252629

‎test/t/codegen.rb‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,3 +608,62 @@ def pattern_over_operand(a)
608608
end
609609
assert_equal :match, matched
610610
end
611+
612+
assert('keyword pairs after a double splat or a flush go into one hash') do
613+
# The pairs left over once a `**` or a register flush had started the hash
614+
# were given a hash of their own when they outnumbered the operand, and the
615+
# callee read the first one: the pairs were dropped, and the block slot the
616+
# call had counted on held the second hash instead of the block.
617+
def codegen_kw(**k, &b) [k, b && b.call(:o)] end
618+
h = {z: 0}
619+
k, r = codegen_kw(**h, a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8,
620+
i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15) { :blk }
621+
assert_equal 16, k.size
622+
assert_equal 15, k[:o]
623+
assert_equal :blk, r
624+
625+
class CodegenKwParent
626+
def m(**k) k end
627+
end
628+
class CodegenKwChild < CodegenKwParent
629+
def m(h)
630+
super(**h, a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8,
631+
i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15)
632+
end
633+
end
634+
assert_equal 16, CodegenKwChild.new.m(h).size
635+
636+
def codegen_kw_yield(h)
637+
yield(**h, a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8,
638+
i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15)
639+
end
640+
assert_equal 16, codegen_kw_yield(h) { |**k| k }.size
641+
642+
# no `**`: the flush at the register limit starts the hash
643+
k, = codegen_kw(k0: 0, k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6, k7: 7, k8: 8,
644+
k9: 9, k10: 10, k11: 11, k12: 12, k13: 13, k14: 14, k15: 15, k16: 16, k17: 17,
645+
k18: 18, k19: 19, k20: 20, k21: 21, k22: 22, k23: 23, k24: 24, k25: 25, k26: 26,
646+
k27: 27, k28: 28, k29: 29, k30: 30, k31: 31, k32: 32, k33: 33, k34: 34, k35: 35,
647+
k36: 36, k37: 37, k38: 38, k39: 39, k40: 40, k41: 41, k42: 42, k43: 43, k44: 44,
648+
k45: 45, k46: 46, k47: 47, k48: 48, k49: 49, k50: 50, k51: 51, k52: 52, k53: 53,
649+
k54: 54, k55: 55, k56: 56, k57: 57, k58: 58, k59: 59, k60: 60, k61: 61, k62: 62)
650+
assert_equal 63, k.size
651+
assert_equal 62, k[:k62]
652+
653+
# a literal in a frame 64 registers deep, where nothing flushes: the second
654+
# hash shifted the gathered arguments by one, and the callee took its first
655+
# argument for the array holding them
656+
def codegen_kw_last(*a) a.last end
657+
k = codegen_kw_last(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
658+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
659+
{**h, k0: 0, k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6, k7: 7, k8: 8,
660+
k9: 9, k10: 10, k11: 11, k12: 12, k13: 13, k14: 14, k15: 15, k16: 16, k17: 17,
661+
k18: 18, k19: 19, k20: 20, k21: 21, k22: 22, k23: 23, k24: 24, k25: 25, k26: 26,
662+
k27: 27, k28: 28, k29: 29, k30: 30, k31: 31, k32: 32, k33: 33, k34: 34, k35: 35,
663+
k36: 36, k37: 37, k38: 38, k39: 39, k40: 40, k41: 41, k42: 42, k43: 43, k44: 44,
664+
k45: 45, k46: 46, k47: 47, k48: 48, k49: 49, k50: 50, k51: 51, k52: 52, k53: 53,
665+
k54: 54, k55: 55, k56: 56, k57: 57, k58: 58, k59: 59, k60: 60, k61: 61, k62: 62,
666+
k63: 63, k64: 64})
667+
assert_equal 66, k.size
668+
assert_equal 64, k[:k64]
669+
end

0 commit comments

Comments
 (0)