|
| 1 | +use v6; |
| 2 | +use Test; |
| 3 | +use soft; |
| 4 | +plan 13; |
| 5 | +my @order; |
| 6 | + |
| 7 | +my class C1 { |
| 8 | + method foo(|) { @order.push: ::?CLASS.^name } |
| 9 | +} |
| 10 | + |
| 11 | +my class C2 is C1 { |
| 12 | + proto method foo(|) {*} |
| 13 | + multi method foo(Str $s) { |
| 14 | + @order.push: ::?CLASS.^name ~ "(Str)"; |
| 15 | + nextsame; |
| 16 | + } |
| 17 | + multi method foo(Int $s) { |
| 18 | + @order.push: ::?CLASS.^name ~ "(Int)"; |
| 19 | + nextsame; |
| 20 | + } |
| 21 | + multi method foo(Num) { |
| 22 | + @order.push: ::?CLASS.^name ~ "(Num)"; |
| 23 | + nextsame |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +my class C3 is C2 { |
| 28 | + method foo(|) { |
| 29 | + @order.push: ::?CLASS.^name; |
| 30 | + nextsame |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +my class C4 is C3 { |
| 35 | + proto method foo(|) {*} |
| 36 | + multi method foo(Int:D $v) { |
| 37 | + @order.push: ::?CLASS.^name ~ "(Int:D)"; |
| 38 | + nextwith ~$v |
| 39 | + } |
| 40 | + multi method foo(Any) { |
| 41 | + @order.push: ::?CLASS.^name ~ "(Any)"; |
| 42 | + callsame |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +my $inst; |
| 47 | + |
| 48 | +$inst = C3.new; |
| 49 | +$inst.foo("bar"); |
| 50 | +is-deeply @order.List, <C3 C2(Str) C1>, "a multi-method doesn't break MRO dispatching"; |
| 51 | +@order = []; |
| 52 | +$inst.foo(42); |
| 53 | +is-deeply @order.List, <C3 C2(Int) C1>, "a multi-method dispatching works correctly"; |
| 54 | + |
| 55 | +$inst = C4.new; |
| 56 | +@order = []; |
| 57 | +$inst.foo("baz"); |
| 58 | +is-deeply @order.List, <C4(Any) C3 C2(Str) C1>, "multi being the first method in MRO still works"; |
| 59 | +@order = []; |
| 60 | +$inst.foo(13); |
| 61 | +is-deeply @order.List, <C4(Int:D) C4(Any) C3 C2(Str) C1>, "nextwith does what's expected"; |
| 62 | + |
| 63 | +my \proto := C2.^find_method('foo', :local, :no_fallback); |
| 64 | + |
| 65 | +nok proto.is_wrapped, "proto is not wrapped yet"; |
| 66 | +my $wh1 = proto.wrap(my method foo-wrap(|) { @order.push: "foo-proto"; nextsame }); |
| 67 | +ok proto.is_wrapped, "proto is wrapped now"; |
| 68 | + |
| 69 | +@order = []; |
| 70 | +$inst.foo(""); |
| 71 | +is-deeply @order.List, <C4(Any) C3 foo-proto C2(Str) C1>, "proto can be wrapped"; |
| 72 | + |
| 73 | +proto.unwrap($wh1); |
| 74 | +@order = []; |
| 75 | +$inst.foo(""); |
| 76 | +is-deeply @order.List, <C4(Any) C3 C2(Str) C1>, "proto can be unwrapped"; |
| 77 | + |
| 78 | +# This should be foo(Rat) candidate |
| 79 | +my \cand = proto.candidates[2]; |
| 80 | +# Note that next* can't be used with blocks. |
| 81 | +$wh1 = cand.wrap(-> *@ { @order.push('foo-num-wrap'); callsame }); |
| 82 | +@order = []; |
| 83 | +$inst.foo(pi); |
| 84 | +is-deeply @order.List, <C4(Any) C3 foo-num-wrap C2(Num) C1>, "we can wrap a candidate"; |
| 85 | + |
| 86 | +# We can even wrap a candidate with another multi. It works! |
| 87 | +proto multi-wrap(|) {*} |
| 88 | +multi multi-wrap(\SELF, Num) { |
| 89 | + @order.push: "multi-wrap(Num)"; |
| 90 | + nextsame |
| 91 | +} |
| 92 | +multi multi-wrap(\SELF, Any) { |
| 93 | + @order.push: "multi-wrap(Any)"; |
| 94 | + nextsame |
| 95 | +} |
| 96 | + |
| 97 | +my $wh2 = cand.wrap(&multi-wrap); |
| 98 | +@order = []; |
| 99 | +$inst.foo(pi); |
| 100 | +is-deeply @order.List, <C4(Any) C3 multi-wrap(Num) multi-wrap(Any) foo-num-wrap C2(Num) C1>, "we can use a multi as a wrapper of a candidate"; |
| 101 | + |
| 102 | +cand.unwrap($wh1); |
| 103 | +@order = []; |
| 104 | +$inst.foo(pi); |
| 105 | +is-deeply @order.List, <C4(Any) C3 multi-wrap(Num) multi-wrap(Any) C2(Num) C1>, "we can use a multi as a wrapper of a candidate"; |
| 106 | + |
| 107 | +# Even nastier thing: wrap a candidate of our wrapper! |
| 108 | +my $wwh = &multi-wrap.candidates[1].wrap(sub (|) { @order.push: 'cand-wrap'; nextsame }); |
| 109 | +@order = []; |
| 110 | +$inst.foo(pi); |
| 111 | +is-deeply @order.List, <C4(Any) C3 multi-wrap(Num) cand-wrap multi-wrap(Any) C2(Num) C1>, "we can use a multi as a wrapper of a candidate"; |
| 112 | + |
| 113 | +# Unwrap the method candidate from the second wrapper. We then get the original behavior. |
| 114 | +cand.unwrap($wh2); |
| 115 | +@order = []; |
| 116 | +$inst.foo(pi); |
| 117 | +is-deeply @order.List, <C4(Any) C3 C2(Num) C1>, "we can use a multi as a wrapper of a candidate"; |
| 118 | + |
| 119 | +done-testing; |
0 commit comments