File Coverage

blib/lib/REFECO/Blockchain/Contract/Solidity/ABI/Type.pm
Criterion Covered Total %
statement 113 115 98.2
branch 33 34 97.0
condition 8 8 100.0
subroutine 26 28 92.8
pod 9 22 40.9
total 189 207 91.3


line stmt bran cond sub pod time code
1              
2             use strict;
3 5     5   30 use warnings;
  5         11  
  5         129  
4 5     5   199 no indirect;
  5         14  
  5         110  
5 5     5   29  
  5         10  
  5         22  
6             use Carp;
7 5     5   193 use Module::Load;
  5         8  
  5         1910  
8 5     5   3244 use constant NOT_IMPLEMENTED => 'Method not implemented';
  5         5614  
  5         28  
9 5     5   269  
  5         9  
  5         10018  
10             my ($class, %params) = @_;
11              
12 164     164 0 479 my $self = bless {}, $class;
13             $self->{signature} = $params{signature};
14 164         374 $self->{data} = $params{data};
15 164         444  
16 164         268 $self->configure();
17              
18 164         465 return $self;
19             }
20 164         665  
21              
22             croak NOT_IMPLEMENTED;
23       111 0   }
24              
25             croak NOT_IMPLEMENTED;
26 0     0 0 0 }
27              
28             return shift->{static} //= [];
29             }
30 0     0 0 0  
31             my ($self, $data) = @_;
32             push($self->static->@*, ref $data eq 'ARRAY' ? $data->@* : $data);
33             }
34 663   100 663 0 1950  
35             return shift->{dynamic} //= [];
36             }
37              
38 122     122 0 221 my ($self, $data) = @_;
39 122 100       239 push($self->dynamic->@*, ref $data eq 'ARRAY' ? $data->@* : $data);
40             }
41              
42             return shift->{signature};
43 607   100 607 0 1636 }
44              
45             return shift->{data};
46             }
47 66     66 0 126  
48 66 100       110 =head2 fixed_length
49              
50             No documentation for perl function 'Get' found
51              
52 733     733 1 3646 =over4
53              
54             =back
55              
56 234     234 1 1288 Return the int length or undef
57              
58             =cut
59              
60             my $self = shift;
61             if ($self->signature =~ /[a-z](\d+)/) {
62             return $1;
63             }
64             return undef;
65             }
66              
67             =head2 pad_right
68              
69             Pad data with right zeros, if the length is bigger than 32 bytes will break
70             the data in chunks of 32 bytes and pad the last chunk
71              
72 42     42 1 64 =over4
73 42 100       84  
74 28         107 =item * C<$data> value to be padded
75              
76 14         38 =back
77              
78             Array ref
79              
80             =cut
81              
82             my ($self, $data) = @_;
83              
84             my @chunks;
85             push(@chunks, $_ . '0' x (64 - length $_)) for unpack("(A64)*", $data);
86              
87             return \@chunks;
88             }
89              
90             =head2 pad_right
91              
92             Pad data with left zeros, if the length is bigger than 32 bytes will break
93             the data in chunks of 32 bytes and pad the first chunk
94              
95 16     16 1 36 =over4
96              
97 16         21 =item * C<$data> value to be padded
98 16         98  
99             =back
100 16         52  
101             Array ref
102              
103             =cut
104              
105             my ($self, $data) = @_;
106              
107             my @chunks;
108             push(@chunks, sprintf("%064s", $_)) for unpack("(A64)*", $data);
109              
110             return \@chunks;
111              
112             }
113              
114             =head2 encode_length
115              
116             Encodes integer length to hex and pad it with left zeros
117              
118             =over4
119 42     42 0 3112  
120             =item * C<$length> value to be encoded
121 42         70  
122 42         243 =back
123              
124 42         148 Encoded hex string
125              
126             =cut
127              
128             my ($self, $length) = @_;
129             return sprintf("%064s", sprintf("%x", $length));
130             }
131              
132             =head2 encode_length
133              
134             Encodes integer offset to hex and pad it with left zeros
135              
136             This expects to receive the non stack offset number e.g. 1,2 instead of 32,64
137             the value will be multiplied by 32, if you need the same without the multiplication
138             use encode_length instead
139              
140             =over4
141              
142             =item * C<$offset> value to be encoded
143 23     23 1 46  
144 23         126 =back
145              
146             Encoded hex string
147              
148             =cut
149              
150             my ($self, $offset) = @_;
151             return sprintf("%064s", sprintf("%x", $offset * 32));
152             }
153              
154             =head2 encoded
155              
156             Join the static and dynamic values
157              
158             =over 4
159              
160             =back
161              
162             Array ref or undef
163              
164             =cut
165              
166 25     25 0 79 my $self = shift;
167 25         122 my @data = ($self->static->@*, $self->dynamic->@*);
168             return scalar @data ? \@data : undef;
169             }
170              
171             =head2 encoded
172              
173             Check if the current type and his instances are dynamic or static
174              
175             =over 4
176              
177             =back
178              
179             1 or 0
180              
181             =cut
182              
183 541     541 1 758 return shift->signature =~ /(bytes|string)(?!\d+)|(\[\])/ ? 1 : 0;
184 541         877 }
185 541 100       1774  
186             =head2 new_type
187              
188             Check if the current type and his instances are dynamic or static
189              
190             =over 4
191              
192             =item * C<%params> type signature as key and data as value
193              
194             =back
195              
196             1 or 0
197              
198             =cut
199              
200             my (%params) = @_;
201 359 100   359 0 610  
202             my $signature = $params{signature};
203              
204             my $module;
205             if ($signature =~ /\[(\d+)?\]$/gm) {
206             $module = "Array";
207             } elsif ($signature =~ /^\(.*\)/) {
208             $module = "Tuple";
209             } elsif ($signature =~ /^address$/) {
210             $module = "Address";
211             } elsif ($signature =~ /^(u)?(int|bool)(\d+)?$/) {
212             $module = "Int";
213             } elsif ($signature =~ /^(?:bytes)(\d+)?$/) {
214             $module = "Bytes";
215             } elsif ($signature =~ /^string$/) {
216             $module = "String";
217             } else {
218             croak "Module not found for the given parameter signature $signature";
219 144     144 1 364 }
220              
221 144         271 # this is just to avoid `use module` for every new type included
222             my $_package = __PACKAGE__;
223 144         215 my $package = sprintf("%s::%s", $_package, $module);
224 144 100       1068 load $package;
    100          
    100          
    100          
    100          
    100          
225 20         39 return $package->new(
226             signature => $signature,
227 12         24 data => $params{data});
228             }
229 22         63  
230             return shift->{instances} //= [];
231 63         119 }
232              
233 21         41 =head2 get_initial_offset
234              
235 5         11 Based in the static items and the offsets in the header gets the first position
236             in the stack for the dynamic values
237 1         77  
238             =over 4
239              
240             =back
241 143         207  
242 143         461 Integer offset
243 143         514  
244             =cut
245              
246 143         7150 my $self = shift;
247             my $offset = 0;
248             for my $param ($self->instances->@*) {
249             my $encoded = $param->encode;
250 391   100 391 0 6720 if ($param->is_dynamic) {
251             $offset += 1;
252             } else {
253             $offset += scalar $param->encoded->@*;
254             }
255             }
256              
257             return $offset;
258             }
259              
260             return 1;
261             }
262              
263             =head2 read_stack_set_data
264              
265             Based in the given signatures and data separate the string data into chunks
266             of instances and decode them
267 38     38 1 63  
268 38         72 =over 4
269 38         76  
270 88         250 =back
271 82 100       243  
272 25         59 Array ref containing the decoded data values
273              
274 57         118 =cut
275              
276             my $self = shift;
277              
278 32         103 my @data = $self->data->@*;
279             my @offsets;
280             my $current_offset = 0;
281              
282 66     66 0 121 # Since at this point we don't information about the chunks of data it is_dynamic
283             # needed to get all the offsets in the static header, so the dynamic values can
284             # be retrieved based in between the current and the next offsets
285             for my $instance ($self->instances->@*) {
286             if ($instance->is_dynamic) {
287             push @offsets, hex($data[$current_offset]) / 32;
288             }
289              
290             my $size = 1;
291             $size = $instance->static_size unless $instance->is_dynamic;
292             $current_offset += $size;
293             }
294              
295             $current_offset = 0;
296             my %response;
297             # Dynamic data must to be set first since the full_size method
298             # will need to use the data offset related to the size of the item
299 14     14 1 22 for (my $i = 0; $i < $self->instances->@*; $i++) {
300             my $instance = $self->instances->[$i];
301 14         30 next unless $instance->is_dynamic;
302 14         24 my $offset_start = shift @offsets;
303 14         22 my $offset_end = $offsets[0] // scalar @data - 1;
304             my @range = @data[$offset_start .. $offset_end];
305             $instance->{data} = \@range;
306             $current_offset += scalar @range;
307             $response{$i} = $instance->decode();
308 14         26 }
309 39 100       76  
310 8         37 $current_offset = 0;
311              
312             for (my $i = 0; $i < $self->instances->@*; $i++) {
313 39         64 my $instance = $self->instances->[$i];
314 39 100       60  
315 39         66 if ($instance->is_dynamic) {
316             $current_offset++;
317             next;
318 14         23 }
319 14         19  
320             my $size = 1;
321             $size = $instance->static_size unless $instance->is_dynamic;
322 14         30 my @range = @data[$current_offset .. $current_offset + $size - 1];
323 39         57 $instance->{data} = \@range;
324 39 100       62 $current_offset += $size;
325 8         17  
326 8   100     28 $response{$i} = $instance->decode();
327 8         31 }
328 8         17  
329 8         11 my @array_response;
330 8         24 # the given order of type signatures needs to be strict followed
331             push(@array_response, $response{$_}) for 0 .. scalar $self->instances->@* - 1;
332             return \@array_response;
333 14         22 }
334              
335 14         26 1;
336 39         65