Skip to content

Commit

Permalink
feat: add .copy() to arc4.Tuple
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-makerx committed Nov 8, 2024
1 parent 0c31697 commit fe7a0ea
Show file tree
Hide file tree
Showing 45 changed files with 1,821 additions and 293 deletions.
4 changes: 2 additions & 2 deletions examples/sizes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
arc4_types/Arc4StringTypes 455 35 - | 245 13 -
arc4_types/Arc4StructsFromAnotherModule 67 12 - | 49 6 -
arc4_types/Arc4StructsType 389 239 - | 259 121 -
arc4_types/Arc4TuplesType 795 136 - | 537 58 -
arc4_types/Arc4TuplesType 954 173 - | 657 77 -
arc4_types/MutableParams2 334 202 - | 193 97 -
arc_28/EventEmitter 186 133 - | 100 64 -
asset/Reference 268 261 - | 144 141 -
Expand Down Expand Up @@ -133,4 +133,4 @@
unssa/UnSSA 432 368 - | 241 204 -
voting/VotingRoundApp 1590 1483 - | 733 649 -
with_reentrancy/WithReentrancy 255 242 - | 132 122 -
Total 70460 54247 54188 | 33630 22152 22108
Total 70619 54284 54225 | 33750 22171 22127
4 changes: 3 additions & 1 deletion src/puyapy/awst_build/eb/arc4/tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from puyapy.awst_build.eb._base import GenericTypeBuilder
from puyapy.awst_build.eb._bytes_backed import BytesBackedInstanceExpressionBuilder
from puyapy.awst_build.eb._utils import compare_bytes, constant_bool_and_error, dummy_value
from puyapy.awst_build.eb.arc4._base import ARC4TypeBuilder
from puyapy.awst_build.eb.arc4._base import ARC4TypeBuilder, CopyBuilder
from puyapy.awst_build.eb.factories import builder_for_instance
from puyapy.awst_build.eb.interface import (
BuilderComparisonOp,
Expand Down Expand Up @@ -118,6 +118,8 @@ def member_access(self, name: str, location: SourceLocation) -> NodeBuilder:
source_location=location,
)
return TupleExpressionBuilder(result_expr, native_pytype)
case "copy":
return CopyBuilder(self.resolve(), location, self.pytype)
case _:
return super().member_access(name, location)

Expand Down
3 changes: 3 additions & 0 deletions stubs/algopy-stubs/arc4.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,9 @@ class Tuple(_ABIEncoded, tuple[typing.Unpack[_TTuple]]):
"""Convert to a native Python tuple - note that the elements of the tuple
should be considered to be copies of the original elements"""

def copy(self) -> typing.Self:
"""Create a copy of this tuple"""

@typing.dataclass_transform(
eq_default=False, order_default=False, kw_only_default=False, field_specifiers=()
)
Expand Down
52 changes: 47 additions & 5 deletions test_cases/arc4_types/out/Arc4TuplesTypeContract.approval.mir
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,23 @@ main_block@0:
== tmp%20#0
assert
// arc4_types/tuples.py:49
// self.test_copy()
callsub test_copy
// arc4_types/tuples.py:51
// return True
int 1 1
return


// test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_stuff(test_tuple: bytes) -> uint64, bytes:
test_stuff: (𝕡) test_tuple#0 |
// arc4_types/tuples.py:54-55
// arc4_types/tuples.py:56-57
// @subroutine
// def test_stuff(self, test_tuple: TestTuple) -> tuple[UInt64, String]:
proto 1 2 (𝕡) test_tuple#0 |

test_stuff_block@0: (𝕡) test_tuple#0 |
// arc4_types/tuples.py:56
// arc4_types/tuples.py:58
// a, b, c, d, e = test_tuple.native
p-load test_tuple#0 (𝕡) test_tuple#0 | test_tuple#0 (copy)
extract 0 1 // on error: Index access is out of bounds (𝕡) test_tuple#0 | a#0
Expand All @@ -57,7 +60,7 @@ test_stuff_block@0:
substring3 (𝕡) test_tuple#0 | a#0,b#0,c#0,d#0
p-load test_tuple#0 (𝕡) test_tuple#0 | a#0,b#0,c#0,d#0,test_tuple#0 (copy)
extract 6 1 // on error: Index access is out of bounds (𝕡) test_tuple#0 | a#0,b#0,c#0,d#0,e#0
// arc4_types/tuples.py:62
// arc4_types/tuples.py:64
// total = a.native + b.native + e.native
l-load a#0 4 (𝕡) test_tuple#0 | b#0,c#0,d#0,e#0,a#0
btoi (𝕡) test_tuple#0 | b#0,c#0,d#0,e#0,tmp%4#0
Expand All @@ -71,7 +74,7 @@ test_stuff_block@0:
l-load tmp%6#0 1 (𝕡) test_tuple#0 | c#0,d#0,tmp%7#0,tmp%6#0
l-load tmp%7#0 1 (𝕡) test_tuple#0 | c#0,d#0,tmp%6#0,tmp%7#0
+ (𝕡) test_tuple#0 | c#0,d#0,total#0
// arc4_types/tuples.py:63
// arc4_types/tuples.py:65
// text = c.native + " " + d.native
l-load c#0 2 (𝕡) test_tuple#0 | d#0,total#0,c#0
extract 2 0 (𝕡) test_tuple#0 | d#0,total#0,tmp%8#0
Expand All @@ -82,7 +85,7 @@ test_stuff_block@0:
l-load tmp%9#0 1 (𝕡) test_tuple#0 | total#0,tmp%10#0,tmp%9#0
l-load tmp%10#0 1 (𝕡) test_tuple#0 | total#0,tmp%9#0,tmp%10#0
concat (𝕡) test_tuple#0 | total#0,text#0
// arc4_types/tuples.py:65
// arc4_types/tuples.py:67
// return total, String(text)
l-load-copy text#0 0 (𝕡) test_tuple#0 | total#0,text#0,text#0 (copy)
len (𝕡) test_tuple#0 | total#0,text#0,length%0#0
Expand All @@ -95,3 +98,42 @@ test_stuff_block@0:
retsub total#0,encoded_value%0#0


// test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_copy() -> void:
test_copy:
// arc4_types/tuples.py:69-70
// @subroutine
// def test_copy(self) -> None:
proto 0 0

test_copy_block@0:
// arc4_types/tuples.py:74
// assert tup[1] == DynamicBytes(0)
byte 0x000100 0x000100
// arc4_types/tuples.py:77
// tup[1][0] = Byte(1)
byte 0x01 0x000100,0x01
replace2 2 updated_target%0#0
byte 0x000003 updated_target%0#0,0x000003
l-load updated_target%0#0 1 0x000003,updated_target%0#0
concat tup#2
// arc4_types/tuples.py:79
// assert tup[1] != tup2[1]
l-load-copy tup#2 0 tup#2,tup#2 (copy)
int 1 tup#2,tup#2 (copy),1
extract_uint16 tup#2,item_start_offset%3#0
l-load-copy tup#2 1 tup#2,item_start_offset%3#0,tup#2 (copy)
len tup#2,item_start_offset%3#0,item_end_offset%3#0
l-load tup#2 2 item_start_offset%3#0,item_end_offset%3#0,tup#2
l-load item_start_offset%3#0 2 item_end_offset%3#0,tup#2,item_start_offset%3#0
l-load item_end_offset%3#0 2 tup#2,item_start_offset%3#0,item_end_offset%3#0
substring3 tmp%4#0
// arc4_types/tuples.py:74
// assert tup[1] == DynamicBytes(0)
byte 0x000100 tmp%4#0,0x000100
// arc4_types/tuples.py:79
// assert tup[1] != tup2[1]
!= tmp%6#0
assert
retsub


48 changes: 43 additions & 5 deletions test_cases/arc4_types/out/Arc4TuplesTypeContract.approval.teal
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma version 10

test_cases.arc4_types.tuples.Arc4TuplesTypeContract.approval_program:
bytecblock 0x000100
// arc4_types/tuples.py:15
// my_tuple = Tuple((UInt8(1), UInt8(2), String("hello"), String("world"), UInt8(255)))
pushbytes 0x01020007000eff000568656c6c6f0005776f726c64
Expand All @@ -19,18 +20,21 @@ test_cases.arc4_types.tuples.Arc4TuplesTypeContract.approval_program:
==
assert
// arc4_types/tuples.py:49
// self.test_copy()
callsub test_copy
// arc4_types/tuples.py:51
// return True
pushint 1 // 1
return


// test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_stuff(test_tuple: bytes) -> uint64, bytes:
test_stuff:
// arc4_types/tuples.py:54-55
// arc4_types/tuples.py:56-57
// @subroutine
// def test_stuff(self, test_tuple: TestTuple) -> tuple[UInt64, String]:
proto 1 2
// arc4_types/tuples.py:56
// arc4_types/tuples.py:58
// a, b, c, d, e = test_tuple.native
frame_dig -1
extract 0 1 // on error: Index access is out of bounds
Expand All @@ -54,7 +58,7 @@ test_stuff:
substring3
frame_dig -1
extract 6 1 // on error: Index access is out of bounds
// arc4_types/tuples.py:62
// arc4_types/tuples.py:64
// total = a.native + b.native + e.native
uncover 4
btoi
Expand All @@ -64,7 +68,7 @@ test_stuff:
swap
btoi
+
// arc4_types/tuples.py:63
// arc4_types/tuples.py:65
// text = c.native + " " + d.native
uncover 2
extract 2 0
Expand All @@ -73,7 +77,7 @@ test_stuff:
uncover 2
extract 2 0
concat
// arc4_types/tuples.py:65
// arc4_types/tuples.py:67
// return total, String(text)
dup
len
Expand All @@ -82,3 +86,37 @@ test_stuff:
swap
concat
retsub


// test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_copy() -> void:
test_copy:
// arc4_types/tuples.py:69-70
// @subroutine
// def test_copy(self) -> None:
proto 0 0
// arc4_types/tuples.py:74
// assert tup[1] == DynamicBytes(0)
bytec_0 // 0x000100
// arc4_types/tuples.py:77
// tup[1][0] = Byte(1)
pushbytes 0x01
replace2 2
pushbytes 0x000003
swap
concat
// arc4_types/tuples.py:79
// assert tup[1] != tup2[1]
dup
pushint 1 // 1
extract_uint16
dig 1
len
substring3
// arc4_types/tuples.py:74
// assert tup[1] == DynamicBytes(0)
bytec_0 // 0x000100
// arc4_types/tuples.py:79
// assert tup[1] != tup2[1]
!=
assert
retsub
2 changes: 1 addition & 1 deletion test_cases/arc4_types/out/Arc4TuplesTypeContract.clear.mir
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Op Stack (out)
// test_cases.arc4_types.tuples.Arc4TuplesTypeContract.clear_state_program() -> uint64:
main_block@0:
// arc4_types/tuples.py:52
// arc4_types/tuples.py:54
// return True
int 1 1
return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma version 10

test_cases.arc4_types.tuples.Arc4TuplesTypeContract.clear_state_program:
// arc4_types/tuples.py:52
// arc4_types/tuples.py:54
// return True
pushint 1 // 1
return
16 changes: 14 additions & 2 deletions test_cases/arc4_types/out/Arc4TuplesTypeContract.destructured.ir
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ contract test_cases.arc4_types.tuples.Arc4TuplesTypeContract:
(assert tmp%19#0)
let tmp%20#0: bool = (== total#0 258u)
(assert tmp%20#0)
test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_copy()
return 1u

subroutine test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_stuff(test_tuple: bytes) -> <uint64, bytes>:
block@0: // L54
block@0: // L56
let a#0: bytes = ((extract 0 1) test_tuple#0) // on error: Index access is out of bounds
let b#0: bytes = ((extract 1 1) test_tuple#0) // on error: Index access is out of bounds
let item_start_offset%0#0: uint64 = (extract_uint16 test_tuple#0 2u)
Expand All @@ -34,8 +35,19 @@ contract test_cases.arc4_types.tuples.Arc4TuplesTypeContract:
let length_uint16%0#0: bytes = ((extract 6 2) as_bytes%0#0)
let encoded_value%0#0: bytes = (concat length_uint16%0#0 text#0)
return total#0 encoded_value%0#0

subroutine test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_copy() -> void:
block@0: // L69
let updated_target%0#0: bytes = ((replace2 2) 0x000100 0x01)
let tup#2: bytes = (concat 0x000003 updated_target%0#0)
let item_start_offset%3#0: uint64 = (extract_uint16 tup#2 1u)
let item_end_offset%3#0: uint64 = (len tup#2)
let tmp%4#0: bytes = (substring3 tup#2 item_start_offset%3#0 item_end_offset%3#0)
let tmp%6#0: bool = (!= tmp%4#0 0x000100)
(assert tmp%6#0)
return

program clear-state:
subroutine test_cases.arc4_types.tuples.Arc4TuplesTypeContract.clear_state_program() -> bool:
block@0: // L51
block@0: // L53
return 1u
68 changes: 66 additions & 2 deletions test_cases/arc4_types/out/Arc4TuplesTypeContract.ssa.ir
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,11 @@ contract test_cases.arc4_types.tuples.Arc4TuplesTypeContract:
(assert tmp%19#0)
let tmp%20#0: bool = (== total#0 258u)
(assert tmp%20#0)
test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_copy()
return 1u

subroutine test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_stuff(test_tuple: bytes) -> <uint64, bytes>:
block@0: // L54
block@0: // L56
let item0%0#0: bytes = (extract3 test_tuple#0 0u 1u) // on error: Index access is out of bounds
let item1%0#0: bytes = (extract3 test_tuple#0 1u 1u) // on error: Index access is out of bounds
let item_start_offset%0#0: uint64 = (extract_uint16 test_tuple#0 2u)
Expand Down Expand Up @@ -260,8 +261,71 @@ contract test_cases.arc4_types.tuples.Arc4TuplesTypeContract:
let length_uint16%0#0: bytes = ((extract 6 2) as_bytes%0#0)
let encoded_value%0#0: bytes = (concat length_uint16%0#0 text#0)
return total#0 encoded_value%0#0

subroutine test_cases.arc4_types.tuples.Arc4TuplesTypeContract.test_copy() -> void:
block@0: // L69
let length%0#0: uint64 = (len 0x)
let as_bytes%0#0: bytes = (itob length%0#0)
let length_uint16%0#0: bytes = ((extract 6 2) as_bytes%0#0)
let encoded_value%0#0: bytes = (concat length_uint16%0#0 0x)
let current_tail_offset%0#0: uint64 = 3u
let encoded_tuple_buffer%0#0: bytes = 0x
let encoded_tuple_buffer%1#0: bytes = (concat encoded_tuple_buffer%0#0 0x00)
let as_bytes%1#0: bytes = (itob current_tail_offset%0#0)
let offset_as_uint16%0#0: bytes = ((extract 6 2) as_bytes%1#0)
let encoded_tuple_buffer%2#0: bytes = (concat encoded_tuple_buffer%1#0 offset_as_uint16%0#0)
let data_length%0#0: uint64 = (len encoded_value%0#0)
let current_tail_offset%1#0: uint64 = (+ current_tail_offset%0#0 data_length%0#0)
let encoded_tuple_buffer%3#0: bytes = (concat encoded_tuple_buffer%2#0 encoded_value%0#0)
let tup#0: bytes = encoded_tuple_buffer%3#0
let item_start_offset%0#0: uint64 = (extract_uint16 tup#0 1u)
let item_end_offset%0#0: uint64 = (len tup#0)
let tmp%0#0: bytes = (substring3 tup#0 item_start_offset%0#0 item_end_offset%0#0)
let expr_value_trimmed%0#0: bytes = ((extract 2 0) tmp%0#0)
let data%0#0: bytes = (concat 0x 0x00)
let concatenated%0#0: bytes = (concat expr_value_trimmed%0#0 data%0#0)
let len_%0#0: uint64 = (len concatenated%0#0)
let as_bytes%2#0: bytes = (itob len_%0#0)
let len_16_bit%0#0: bytes = ((extract 6 2) as_bytes%2#0)
let concat_result%0#0: bytes = (concat len_16_bit%0#0 concatenated%0#0)
let assigned_value%0#0: bytes = concat_result%0#0
let item_offset%0#0: uint64 = (extract_uint16 tup#0 1u)
let data_up_to_item%0#0: bytes = (extract3 tup#0 0u item_offset%0#0)
let updated_data%0#0: bytes = (concat data_up_to_item%0#0 assigned_value%0#0)
let tup#1: bytes = updated_data%0#0
let item_start_offset%1#0: uint64 = (extract_uint16 tup#1 1u)
let item_end_offset%1#0: uint64 = (len tup#1)
let tmp%1#0: bytes = (substring3 tup#1 item_start_offset%1#0 item_end_offset%1#0)
let result%0#0: bytes = (concat 0x 0x00)
let array_data%0#0: bytes = (concat 0x0001 result%0#0)
let tmp%2#0: bool = (== tmp%1#0 array_data%0#0)
(assert tmp%2#0)
let copy%0#0: bytes = tup#1
let tup2#0: bytes = copy%0#0
let item_start_offset%2#0: uint64 = (extract_uint16 tup#1 1u)
let item_end_offset%2#0: uint64 = (len tup#1)
let tmp%3#0: bytes = (substring3 tup#1 item_start_offset%2#0 item_end_offset%2#0)
let assigned_value%1#0: bytes = 0x01
let array_length%0#0: uint64 = (extract_uint16 tmp%3#0 0u)
let index_is_in_bounds%0#0: bool = (< 0u array_length%0#0)
(assert index_is_in_bounds%0#0) // Index access is out of bounds
let updated_target%0#0: bytes = (replace3 tmp%3#0 2u assigned_value%1#0)
let assigned_value%2#0: bytes = updated_target%0#0
let item_offset%1#0: uint64 = (extract_uint16 tup#1 1u)
let data_up_to_item%1#0: bytes = (extract3 tup#1 0u item_offset%1#0)
let updated_data%1#0: bytes = (concat data_up_to_item%1#0 assigned_value%2#0)
let tup#2: bytes = updated_data%1#0
let item_start_offset%3#0: uint64 = (extract_uint16 tup#2 1u)
let item_end_offset%3#0: uint64 = (len tup#2)
let tmp%4#0: bytes = (substring3 tup#2 item_start_offset%3#0 item_end_offset%3#0)
let item_start_offset%4#0: uint64 = (extract_uint16 tup2#0 1u)
let item_end_offset%4#0: uint64 = (len tup2#0)
let tmp%5#0: bytes = (substring3 tup2#0 item_start_offset%4#0 item_end_offset%4#0)
let tmp%6#0: bool = (!= tmp%4#0 tmp%5#0)
(assert tmp%6#0)
return

program clear-state:
subroutine test_cases.arc4_types.tuples.Arc4TuplesTypeContract.clear_state_program() -> bool:
block@0: // L51
block@0: // L53
return 1u
Loading

0 comments on commit fe7a0ea

Please sign in to comment.