| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Test::Mimic::Library; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
74337
|
use 5.006001; # for my $filehandle |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
124
|
|
|
4
|
1
|
|
|
1
|
|
7
|
use strict; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
37
|
|
|
5
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
6
|
|
|
|
1
|
|
|
|
|
155
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = 0.012_006; |
|
8
|
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
992
|
use Test::Mimic::Library::MonitorScalar; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
159
|
|
|
10
|
1
|
|
|
1
|
|
855
|
use Test::Mimic::Library::MonitorArray; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
34
|
|
|
11
|
1
|
|
|
1
|
|
1879
|
use Test::Mimic::Library::MonitorHash; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
34
|
|
|
12
|
1
|
|
|
1
|
|
1126
|
use Test::Mimic::Library::PlayScalar; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
31
|
|
|
13
|
1
|
|
|
1
|
|
1337
|
use Test::Mimic::Library::PlayArray; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
23
|
|
|
14
|
1
|
|
|
1
|
|
1570
|
use Test::Mimic::Library::PlayHash; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
36
|
|
|
15
|
1
|
|
|
1
|
|
741
|
use Test::Mimic::Library::MonitorTiedScalar; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
27
|
|
|
16
|
1
|
|
|
1
|
|
738
|
use Test::Mimic::Library::MonitorTiedArray; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
25
|
|
|
17
|
1
|
|
|
1
|
|
743
|
use Test::Mimic::Library::MonitorTiedHash; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
30
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
7
|
use Scalar::Util qw; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
728
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
#use Data::Dump::Streamer if possible, otherwise Data::Dumper and ad hoc replacements. |
|
22
|
|
|
|
|
|
|
BEGIN { |
|
23
|
1
|
50
|
|
1
|
|
3
|
if ( eval { require Data::Dump::Streamer; 1 } ) { |
|
|
1
|
|
|
|
|
463
|
|
|
|
0
|
|
|
|
|
0
|
|
|
24
|
0
|
|
|
|
|
0
|
Data::Dump::Streamer->import( qw<:undump Dump regex> ); |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# Accepts a single argument. Returns true iff the argument is a regular expression created by qr. |
|
27
|
0
|
|
|
|
|
0
|
*_is_pattern = sub { return scalar regex( $_[0] ); }; |
|
|
0
|
|
|
|
|
0
|
|
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Accepts a single argument. Returns a string form of this argument that can be inverted |
|
30
|
|
|
|
|
|
|
# (approximately) with _default_destringifier. |
|
31
|
|
|
|
|
|
|
*_default_stringifier = sub { |
|
32
|
0
|
|
|
|
|
0
|
return scalar Dump( $_[0] )->Names('TML_destringify_val')->KeyOrder('', 'lexical')->Out(); |
|
33
|
0
|
|
|
|
|
0
|
}; |
|
34
|
|
|
|
|
|
|
# The horrible name is my attempt to avoid collisions with variables from closures. Sadly, DDS doesn't |
|
35
|
|
|
|
|
|
|
# allow package scoped names. |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Accepts a string returned by _default_stringifier. Returns an approximation to the original value. |
|
38
|
|
|
|
|
|
|
*_default_destringifier = sub { |
|
39
|
0
|
|
|
|
|
0
|
my $TML_destringify_val; |
|
40
|
0
|
0
|
|
|
|
0
|
eval( $_[0] . "; 1" ) |
|
41
|
|
|
|
|
|
|
or die "Unable to eval the string: $_[0]\nwith error: $@"; |
|
42
|
0
|
|
|
|
|
0
|
return $TML_destringify_val; |
|
43
|
0
|
|
|
|
|
0
|
}; |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
else { |
|
46
|
1
|
|
|
|
|
1734
|
require Data::Dumper; |
|
47
|
1
|
|
|
|
|
12148
|
Data::Dumper->import(); |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# Accepts a single argument. Returns true if the argument is a regular expression created by qr that |
|
50
|
|
|
|
|
|
|
# is not blessed. If it is blessed returns true iff the argument was blessed into the Regexp class. |
|
51
|
|
|
|
|
|
|
# Returns false in all other cases. In other words, this gives false positives for non qr refs |
|
52
|
|
|
|
|
|
|
# blessed into Regexp and false negatives for qr refs blessed into any other package. |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# NOTE: This is a major problem if we need to store qr refs blessed into other packages. We will |
|
55
|
|
|
|
|
|
|
# attempt to dereference the qr object and tie the result. This will cause our code to die. False |
|
56
|
|
|
|
|
|
|
# positives will merely cause incomplete recording and punt the responsibility of preserving the |
|
57
|
|
|
|
|
|
|
# value to the stringifier. |
|
58
|
|
|
|
|
|
|
*_is_pattern = sub { |
|
59
|
17
|
|
|
17
|
|
34
|
my $type = ref( $_[0] ); |
|
60
|
17
|
50
|
|
|
|
32
|
if ( defined($type) ) { |
|
61
|
17
|
|
|
|
|
41
|
my $class = blessed( $_[0] ); |
|
62
|
17
|
100
|
|
|
|
27
|
if ( defined($class) ) { |
|
63
|
1
|
|
|
|
|
7
|
return $class eq 'Regexp'; |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
else { |
|
66
|
16
|
|
|
|
|
58
|
return $type eq 'Regexp'; |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
else { |
|
70
|
0
|
|
|
|
|
0
|
return (); |
|
71
|
|
|
|
|
|
|
} |
|
72
|
1
|
|
|
|
|
9
|
}; |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# Accepts a single argument. Returns a string form of this argument that can be inverted |
|
75
|
|
|
|
|
|
|
# (approximately) with _default_destringifier. |
|
76
|
1
|
|
|
6
|
|
4
|
*_default_stringifier = sub { return scalar Dumper( $_[0] ); }; |
|
|
6
|
|
|
|
|
24
|
|
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# Accepts a string returned by _default_stringifier. Returns an approximation to the original value. |
|
79
|
|
|
|
|
|
|
*_default_destringifier = sub { |
|
80
|
5
|
|
|
5
|
|
5
|
my $VAR1; |
|
81
|
5
|
50
|
|
|
|
366
|
eval( $_[0] . "; 1" ) |
|
82
|
|
|
|
|
|
|
or die "Unable to eval the string: $_[0]\nwith error: $@"; |
|
83
|
5
|
|
|
|
|
110
|
return $VAR1; |
|
84
|
1
|
|
|
|
|
160
|
}; |
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
require Exporter; |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
our @ISA = qw; |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
|
93
|
|
|
|
|
|
|
'constants' => [ qw( |
|
94
|
|
|
|
|
|
|
SCALAR_CONTEXT |
|
95
|
|
|
|
|
|
|
LIST_CONTEXT |
|
96
|
|
|
|
|
|
|
VOID_CONTEXT |
|
97
|
|
|
|
|
|
|
STABLE |
|
98
|
|
|
|
|
|
|
VOLATILE |
|
99
|
|
|
|
|
|
|
NESTED |
|
100
|
|
|
|
|
|
|
RETURN |
|
101
|
|
|
|
|
|
|
EXCEPTION |
|
102
|
|
|
|
|
|
|
ARBITRARY |
|
103
|
|
|
|
|
|
|
CODE_E |
|
104
|
|
|
|
|
|
|
SCALAR_E |
|
105
|
|
|
|
|
|
|
ARRAY_E |
|
106
|
|
|
|
|
|
|
HASH_E |
|
107
|
|
|
|
|
|
|
ENCODE_TYPE |
|
108
|
|
|
|
|
|
|
DATA |
|
109
|
|
|
|
|
|
|
DATA_TYPE |
|
110
|
|
|
|
|
|
|
HISTORY |
|
111
|
|
|
|
|
|
|
CLASS |
|
112
|
|
|
|
|
|
|
) ], |
|
113
|
|
|
|
|
|
|
); |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
our @EXPORT_OK = ( |
|
116
|
|
|
|
|
|
|
qw< |
|
117
|
|
|
|
|
|
|
encode |
|
118
|
|
|
|
|
|
|
decode |
|
119
|
|
|
|
|
|
|
monitor |
|
120
|
|
|
|
|
|
|
play |
|
121
|
|
|
|
|
|
|
monitor_args |
|
122
|
|
|
|
|
|
|
monitor_args_by |
|
123
|
|
|
|
|
|
|
play_args |
|
124
|
|
|
|
|
|
|
play_args_by |
|
125
|
|
|
|
|
|
|
gen_arg_key |
|
126
|
|
|
|
|
|
|
gen_arg_key_by |
|
127
|
|
|
|
|
|
|
stringify |
|
128
|
|
|
|
|
|
|
stringify_by |
|
129
|
|
|
|
|
|
|
destringify |
|
130
|
|
|
|
|
|
|
destringify_by |
|
131
|
|
|
|
|
|
|
init_records |
|
132
|
|
|
|
|
|
|
load_records |
|
133
|
|
|
|
|
|
|
write_records |
|
134
|
|
|
|
|
|
|
get_references |
|
135
|
|
|
|
|
|
|
execute |
|
136
|
|
|
|
|
|
|
descend |
|
137
|
|
|
|
|
|
|
load_preferences |
|
138
|
|
|
|
|
|
|
>, |
|
139
|
|
|
|
|
|
|
@{ $EXPORT_TAGS{'constants'} }, |
|
140
|
|
|
|
|
|
|
); |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
our @EXPORT = qw( |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
); |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
use constant { |
|
148
|
|
|
|
|
|
|
# Array indices for the three contexts |
|
149
|
1
|
|
|
|
|
5640
|
SCALAR_CONTEXT => 0, |
|
150
|
|
|
|
|
|
|
LIST_CONTEXT => 1, |
|
151
|
|
|
|
|
|
|
VOID_CONTEXT => 2, |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# Description of encoded data |
|
154
|
|
|
|
|
|
|
STABLE => 200, |
|
155
|
|
|
|
|
|
|
VOLATILE => 201, |
|
156
|
|
|
|
|
|
|
NESTED => 202, |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# The two types of supported behavior |
|
159
|
|
|
|
|
|
|
RETURN => 300, |
|
160
|
|
|
|
|
|
|
EXCEPTION => 301, |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# Convenience values |
|
163
|
|
|
|
|
|
|
ARBITRARY => 400, # For merely creating hash entries |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Event types. Should we deprecate this? |
|
166
|
|
|
|
|
|
|
CODE_E => 500, |
|
167
|
|
|
|
|
|
|
SCALAR_E => 501, |
|
168
|
|
|
|
|
|
|
ARRAY_E => 502, |
|
169
|
|
|
|
|
|
|
HASH_E => 503, |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# Encoded data fields, i.e. indices. |
|
172
|
|
|
|
|
|
|
ENCODE_TYPE => 0, |
|
173
|
|
|
|
|
|
|
DATA => 1, |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# Reference table item fields, i.e. indices. |
|
176
|
|
|
|
|
|
|
DATA_TYPE => 0, |
|
177
|
|
|
|
|
|
|
HISTORY => 1, |
|
178
|
|
|
|
|
|
|
CLASS => 2, |
|
179
|
|
|
|
|
|
|
|
|
180
|
1
|
|
|
1
|
|
8
|
}; |
|
|
1
|
|
|
|
|
2
|
|
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
my $references; # A table containing recorded data for volatile references and objects. The index of a |
|
183
|
|
|
|
|
|
|
# given reference is simply the number of references |
|
184
|
|
|
|
|
|
|
# monitor saw before the reference under |
|
185
|
|
|
|
|
|
|
# consideration. |
|
186
|
|
|
|
|
|
|
my $address_to_index; # A hash ref mapping the address of a reference to its index in $references. |
|
187
|
|
|
|
|
|
|
my $is_alive; # A hash ref mapping the address of a reference to its current alive state. This will |
|
188
|
|
|
|
|
|
|
# be defined if the value stored at $address_to_index is current, undefined |
|
189
|
|
|
|
|
|
|
# otherwise. |
|
190
|
|
|
|
|
|
|
my $index_to_reference; # Almost, but not quite, the inverse of $address_to_index. Rather than mapping to the |
|
191
|
|
|
|
|
|
|
# address of the reference it maps to the reference itself. |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
# Preloaded methods go here. |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub init_records { |
|
196
|
1
|
|
|
1
|
1
|
8
|
$references = []; |
|
197
|
1
|
|
|
|
|
3
|
$address_to_index = {}; |
|
198
|
1
|
|
|
|
|
3
|
$is_alive = {}; |
|
199
|
1
|
|
|
|
|
3
|
$index_to_reference = {}; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub load_records { |
|
203
|
0
|
|
|
0
|
1
|
0
|
my ($file_name) = @_; |
|
204
|
|
|
|
|
|
|
|
|
205
|
0
|
|
|
|
|
0
|
init_records(); |
|
206
|
|
|
|
|
|
|
|
|
207
|
0
|
0
|
|
|
|
0
|
open( my $fh, '<', $file_name ) or die "Could not open file: $!"; |
|
208
|
|
|
|
|
|
|
|
|
209
|
0
|
|
|
|
|
0
|
my $recorded_data; |
|
210
|
|
|
|
|
|
|
{ |
|
211
|
0
|
|
|
|
|
0
|
local $/; |
|
|
0
|
|
|
|
|
0
|
|
|
212
|
0
|
|
|
|
|
0
|
undef $/; |
|
213
|
0
|
|
|
|
|
0
|
$recorded_data = <$fh>; |
|
214
|
|
|
|
|
|
|
} |
|
215
|
0
|
|
|
|
|
0
|
$references = destringify($recorded_data); |
|
216
|
|
|
|
|
|
|
|
|
217
|
0
|
0
|
|
|
|
0
|
close($fh) or die "Could not close file: $!"; |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub get_references { |
|
221
|
1
|
|
|
1
|
1
|
58
|
return $references; |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub write_records { |
|
225
|
0
|
|
|
0
|
1
|
0
|
my ($file_name) = @_; |
|
226
|
|
|
|
|
|
|
|
|
227
|
0
|
0
|
|
|
|
0
|
open( my $fh, '>', $file_name ) or die "Could not open file: $!"; |
|
228
|
0
|
|
|
|
|
0
|
print $fh stringify($references); |
|
229
|
0
|
0
|
|
|
|
0
|
close($fh) or die "Could not close file: $!"; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub load_preferences { |
|
233
|
0
|
|
|
0
|
0
|
0
|
my ($preferences) = @_; |
|
234
|
|
|
|
|
|
|
|
|
235
|
0
|
0
|
|
|
|
0
|
if ( defined( $preferences->{'string'} ) ) { |
|
236
|
0
|
|
|
|
|
0
|
stringify_by( $preferences->{'string' } ); |
|
237
|
|
|
|
|
|
|
} |
|
238
|
0
|
0
|
|
|
|
0
|
if ( defined( $preferences->{'destring'} ) ) { |
|
239
|
0
|
|
|
|
|
0
|
destringify_by( $preferences->{'destring'} ); |
|
240
|
|
|
|
|
|
|
} |
|
241
|
0
|
|
|
|
|
0
|
gen_arg_key_by($preferences); |
|
242
|
0
|
|
|
|
|
0
|
monitor_args_by($preferences); |
|
243
|
0
|
|
|
|
|
0
|
play_args_by($preferences); |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# Changes the current working directory to $dir. If $dir does not exist then it will be created. |
|
247
|
|
|
|
|
|
|
# If it exists, but it is not a directory or any other error occurs descend will die. |
|
248
|
|
|
|
|
|
|
sub descend { |
|
249
|
0
|
|
|
0
|
1
|
0
|
my ($dir) = @_; |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# Move to the $dir directory, creating if needed. |
|
252
|
0
|
0
|
|
|
|
0
|
if ( -e $dir ) { |
|
253
|
0
|
0
|
|
|
|
0
|
if ( ! ( -d $dir ) ) { |
|
254
|
0
|
|
|
|
|
0
|
die "$dir exists, but it is not a directory."; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
else { |
|
258
|
0
|
0
|
|
|
|
0
|
mkdir( $dir ) or die "Could not create directory: $!"; |
|
259
|
|
|
|
|
|
|
} |
|
260
|
0
|
0
|
|
|
|
0
|
chdir($dir) or die "Could not change the current working directory: $!"; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub execute { |
|
264
|
0
|
|
|
0
|
1
|
0
|
my ( $package, $subroutine, $behavior, $args ) = @_; |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# Find proper behavior for these arguments. |
|
267
|
0
|
|
|
|
|
0
|
my $key = gen_arg_key( $package, $subroutine, $args ); |
|
268
|
|
|
|
|
|
|
|
|
269
|
0
|
0
|
|
|
|
0
|
if ( ! exists( $behavior->{$key} ) ) { |
|
270
|
0
|
|
|
|
|
0
|
die "No call recorded with corresponding arguments. Package: $package, Subroutine: $subroutine, Key: $key"; |
|
271
|
|
|
|
|
|
|
} |
|
272
|
0
|
|
|
|
|
0
|
my $context_to_result = $behavior->{$key}; |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# Find proper behavior for this context. |
|
275
|
0
|
|
|
|
|
0
|
my $index; |
|
276
|
0
|
0
|
|
|
|
0
|
if (wantarray) { |
|
|
|
0
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
0
|
$index = LIST_CONTEXT; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
elsif ( defined wantarray ) { |
|
280
|
0
|
|
|
|
|
0
|
$index = SCALAR_CONTEXT; |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
else { |
|
283
|
0
|
|
|
|
|
0
|
$index = VOID_CONTEXT; |
|
284
|
|
|
|
|
|
|
} |
|
285
|
0
|
|
|
|
|
0
|
my $results = $context_to_result->[$index]; |
|
286
|
0
|
0
|
|
|
|
0
|
if ( ! defined( $results ) ) { |
|
287
|
0
|
|
|
|
|
0
|
die "No call recorded in context $index. Package: $package, Subroutine: $subroutine, Key: $key"; |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# Obtain the results for this call. |
|
291
|
0
|
0
|
|
|
|
0
|
if ( @{$results} == 0 ) { |
|
|
0
|
|
|
|
|
0
|
|
|
292
|
0
|
|
|
|
|
0
|
die "Call history exhausted. Package: $package, Subroutine: $subroutine, Key: $key"; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
|
|
|
|
|
|
|
|
295
|
0
|
|
|
|
|
0
|
my ( $arg_signature, $stored_result ) = splice( @{$results}, 0, 2 ); |
|
|
0
|
|
|
|
|
0
|
|
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# Tie arguments making them behave as they were recorded behaving. |
|
298
|
0
|
|
|
|
|
0
|
play_args( $package, $subroutine, $args, $arg_signature ); |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# Perform appropriately |
|
301
|
0
|
|
|
|
|
0
|
my ( $result_type, $result ) = @{$stored_result}; |
|
|
0
|
|
|
|
|
0
|
|
|
302
|
0
|
0
|
|
|
|
0
|
if ( $result_type == EXCEPTION ) { |
|
|
|
0
|
|
|
|
|
|
|
303
|
0
|
|
|
|
|
0
|
die decode( $result ); |
|
304
|
|
|
|
|
|
|
} |
|
305
|
|
|
|
|
|
|
elsif ( $result_type == RETURN ) { |
|
306
|
0
|
0
|
|
|
|
0
|
if (wantarray) { |
|
|
|
0
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
0
|
return @{ decode($result) }; |
|
|
0
|
|
|
|
|
0
|
|
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
elsif ( defined wantarray ) { |
|
310
|
0
|
|
|
|
|
0
|
return decode($result); |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
else { |
|
313
|
0
|
|
|
|
|
0
|
return; |
|
314
|
|
|
|
|
|
|
} |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
else { |
|
317
|
0
|
|
|
|
|
0
|
die "Bad result type <$result_type>. Package: $package, Subroutine: $subroutine, Key: $key"; |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
{ |
|
322
|
|
|
|
|
|
|
my $key_gens = {}; |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# The best way to think of the key generator is as a hint to the mimic system. A constant map to |
|
325
|
|
|
|
|
|
|
# 'the key' would work provided that all calls to a given subroutine occur in order. If a smarter |
|
326
|
|
|
|
|
|
|
# map is used then the mimic system will be more flexible. Call order only must be preserved in each set |
|
327
|
|
|
|
|
|
|
# of calls generated by the inverse map of each distinct key. Of course, if one call produces data that |
|
328
|
|
|
|
|
|
|
# another requires it doesn't really make sense to change the order (in either the playback _or_ record |
|
329
|
|
|
|
|
|
|
# stages). |
|
330
|
|
|
|
|
|
|
# |
|
331
|
|
|
|
|
|
|
# NOTE: The passed subroutine should probably not use the stored reference information. This is because |
|
332
|
|
|
|
|
|
|
# out of order calls could then break. Consider subroutines foo and bar. Both take a hash |
|
333
|
|
|
|
|
|
|
# reference. Suppose that in the recording stage foo is called first, bar second and that the same |
|
334
|
|
|
|
|
|
|
# reference is passed both times. If the reference is created by the user, i.e. not returned from a |
|
335
|
|
|
|
|
|
|
# mimicked subroutine or otherwise seen by the recorder, then foo will end up naming the reference. |
|
336
|
|
|
|
|
|
|
# foo's key generator will not be able to include the reference name and will perhaps instead perform |
|
337
|
|
|
|
|
|
|
# a straightforward stringification of the hash. bar's key generator on the other hand will be able to |
|
338
|
|
|
|
|
|
|
# use the fact that we are monitoring the reference and may instead create a key like '[ VOLATILE, 47 ]'. |
|
339
|
|
|
|
|
|
|
# Now suppose that in the playback stage the call order is reversed. The hash reference isn't named until |
|
340
|
|
|
|
|
|
|
# the call to foo, so there is no way bar can recognize it. |
|
341
|
|
|
|
|
|
|
# |
|
342
|
|
|
|
|
|
|
# NOTE: Or maybe SCRATCH ALL OF THAT. The above problem sucks, but the alternative is worse. Suppose we |
|
343
|
|
|
|
|
|
|
# do a _light_encode and then a stringification. If we played the object into existence then it is tied. |
|
344
|
|
|
|
|
|
|
# If it is tied and we examine it we will consume it's output. Even we added logic to halt the |
|
345
|
|
|
|
|
|
|
# consumption we don't have access to the most recent state of the object. Similarly, in the record phase |
|
346
|
|
|
|
|
|
|
# we don't know what the next access will be when gen_arg_key is called, so we can't approximate state |
|
347
|
|
|
|
|
|
|
# by considering the history information. We could allow gen_arg_key to cause history to build up like |
|
348
|
|
|
|
|
|
|
# it was a user call, but then we are enforcing call order on the set of subroutines that share |
|
349
|
|
|
|
|
|
|
# arguments. This is definitely a lesser of, err... 4 or 5, evils situation. |
|
350
|
|
|
|
|
|
|
# |
|
351
|
|
|
|
|
|
|
# NOTE: Additionally, you should avoid any calls to monitor, monitor_args or encode. These have the side |
|
352
|
|
|
|
|
|
|
# effect of naming passed values which will break the built in monitor_args/play_args paradigm. |
|
353
|
|
|
|
|
|
|
sub gen_arg_key_by { |
|
354
|
1
|
|
|
1
|
1
|
1415
|
$key_gens = $_[0]; |
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
sub gen_arg_key { |
|
358
|
4
|
|
|
4
|
1
|
1263
|
my ( $package, $subroutine, $args ) = @_; |
|
359
|
4
|
|
|
|
|
7
|
local $Test::Mimic::Recorder::SuspendRecording = 1; |
|
360
|
|
|
|
|
|
|
|
|
361
|
4
|
|
|
|
|
6
|
my $key_gen; |
|
362
|
4
|
100
|
100
|
|
|
47
|
if ( defined( $key_gen = $key_gens->{'packages'}->{$package}->{'subs'}->{$subroutine}->{'key'} ) |
|
|
|
|
100
|
|
|
|
|
|
363
|
|
|
|
|
|
|
|| defined( $key_gen = $key_gens->{'packages'}->{$package}->{'key'} ) |
|
364
|
|
|
|
|
|
|
|| defined( $key_gen = $key_gens->{'key'} ) ) { |
|
365
|
|
|
|
|
|
|
|
|
366
|
3
|
|
|
|
|
6
|
return &{$key_gen}($args); |
|
|
3
|
|
|
|
|
11
|
|
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
else { |
|
369
|
1
|
|
|
|
|
5
|
return stringify( _light_encode( $args, 2 ) ); |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
} |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
{ |
|
375
|
|
|
|
|
|
|
# Each of these helper subroutines takes ( $val, $at_level, $type ). |
|
376
|
|
|
|
|
|
|
my $scalar_action = sub { return [ 'SCALAR', _light_encode( ${ $_[0] }, $_[1] ) ]; }; |
|
377
|
|
|
|
|
|
|
my $simple_action = sub { return [ $_[2] ]; }; |
|
378
|
|
|
|
|
|
|
my %type_to_action = ( |
|
379
|
|
|
|
|
|
|
'REG_EXP' => $simple_action, |
|
380
|
|
|
|
|
|
|
'SCALAR' => $scalar_action, |
|
381
|
|
|
|
|
|
|
'REF' => $scalar_action, |
|
382
|
|
|
|
|
|
|
'LVALUE' => $scalar_action, |
|
383
|
|
|
|
|
|
|
'VSTRING' => $scalar_action, |
|
384
|
|
|
|
|
|
|
'ARRAY' => sub { |
|
385
|
|
|
|
|
|
|
my @temp = map( { _light_encode( $_, $_[1] ) } @{ $_[0] } ); |
|
386
|
|
|
|
|
|
|
return [ 'ARRAY', \@temp ]; |
|
387
|
|
|
|
|
|
|
}, |
|
388
|
|
|
|
|
|
|
'HASH' => sub { |
|
389
|
|
|
|
|
|
|
my %temp; |
|
390
|
|
|
|
|
|
|
@temp{ keys %{ $_[0] } } = map( { _light_encode( $_[0]->{$_}, $_[1] ) } keys %{ $_[0] } ); |
|
391
|
|
|
|
|
|
|
return [ 'HASH', \%temp]; |
|
392
|
|
|
|
|
|
|
}, |
|
393
|
|
|
|
|
|
|
'GLOB' => $simple_action, |
|
394
|
|
|
|
|
|
|
'IO' => $simple_action, |
|
395
|
|
|
|
|
|
|
'FORMAT' => $simple_action, |
|
396
|
|
|
|
|
|
|
'CODE' => $simple_action, |
|
397
|
|
|
|
|
|
|
); |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# RESULTS NOT SUITABLE FOR DECODE! |
|
400
|
|
|
|
|
|
|
sub _light_encode { |
|
401
|
23
|
|
|
23
|
|
767
|
my ( $val, $at_level ) = @_; |
|
402
|
|
|
|
|
|
|
|
|
403
|
23
|
|
|
|
|
57
|
my $type = reftype($val); |
|
404
|
23
|
100
|
|
|
|
59
|
if ( ! $type ) { # If the value is not a reference... |
|
|
|
50
|
|
|
|
|
|
|
405
|
10
|
|
|
|
|
41
|
return [ STABLE, $val ]; |
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
elsif ( exists( $type_to_action{$type} ) ) { |
|
408
|
13
|
|
|
|
|
29
|
my $address = refaddr($val); |
|
409
|
13
|
50
|
|
|
|
31
|
if ( defined( $is_alive->{$address} ) ) { |
|
410
|
0
|
|
|
|
|
0
|
return [ VOLATILE, $address_to_index->{$address} ]; |
|
411
|
|
|
|
|
|
|
} |
|
412
|
|
|
|
|
|
|
|
|
413
|
13
|
50
|
|
|
|
27
|
if ( _is_pattern($val) ) { # reftype doesn't recognize patterns, so set $type manually. |
|
414
|
0
|
|
|
|
|
0
|
$type = 'REG_EXP'; |
|
415
|
|
|
|
|
|
|
} |
|
416
|
|
|
|
|
|
|
|
|
417
|
13
|
100
|
|
|
|
46
|
if ( $at_level == 0 ) { # If we have reached the deepest requested layer... |
|
418
|
2
|
|
|
|
|
13
|
return [ NESTED, [ $type ] ]; |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
else { |
|
421
|
11
|
|
|
|
|
14
|
$at_level--; |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
|
|
424
|
11
|
|
|
|
|
13
|
my $coded = &{ $type_to_action{$type} }( $val, $at_level, $type ); |
|
|
11
|
|
|
|
|
35
|
|
|
425
|
11
|
|
|
|
|
47
|
return [ NESTED, $coded ]; |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
else { |
|
428
|
0
|
|
|
|
|
0
|
die "Unknown reference type <$type> from <$val>. Unable to encode."; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# So you want to build your own key generator? That's great. One rule: Never ever ever view the state of the |
|
434
|
|
|
|
|
|
|
# arguments you are mapping into keys. That won't be a problem will it? Didn't think so. Those of you |
|
435
|
|
|
|
|
|
|
# nonconformists that think state is important can use get_id. For each component of a passed value, i.e. |
|
436
|
|
|
|
|
|
|
# a single alias in the list, an array element of a dereferenced alias, an element of the array element |
|
437
|
|
|
|
|
|
|
# dereferenced as a hash etc., that you wish to examine _at all_ you must first call get_id on the component. |
|
438
|
|
|
|
|
|
|
# If it returns undef you can look at it, but if it is an aggregate you need to use get_id on it's components |
|
439
|
|
|
|
|
|
|
# as well. If undef is not returned, then you will be given an index corresponding to the reference. It is |
|
440
|
|
|
|
|
|
|
# guaranteed to be unique over the execution of the program and stable between the record and playback |
|
441
|
|
|
|
|
|
|
# phases. This is due to the fact that what you think are real variables in the playback phase are really |
|
442
|
|
|
|
|
|
|
# tied variables. They don't have any state and if you try to look at them you will just consume their fake |
|
443
|
|
|
|
|
|
|
# state. This will cause everything to crash and burn. In conclusion, use get_id. In the future we may store |
|
444
|
|
|
|
|
|
|
# state in the tied variables and allow you to look at them. Keep your fingers crossed. |
|
445
|
|
|
|
|
|
|
sub get_id { |
|
446
|
0
|
|
|
0
|
1
|
0
|
my ($val) = @_; |
|
447
|
|
|
|
|
|
|
|
|
448
|
0
|
|
|
|
|
0
|
my $address = refaddr($val); |
|
449
|
0
|
0
|
|
|
|
0
|
if ( defined( $is_alive->{$address} ) ) { |
|
450
|
0
|
|
|
|
|
0
|
return $address_to_index->{$address}; |
|
451
|
|
|
|
|
|
|
} |
|
452
|
|
|
|
|
|
|
else { |
|
453
|
0
|
|
|
|
|
0
|
return undef; |
|
454
|
|
|
|
|
|
|
} |
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
{ |
|
458
|
|
|
|
|
|
|
my $stringifier = \&_default_stringifier; |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
sub stringify_by { |
|
461
|
1
|
|
|
1
|
1
|
861
|
$stringifier = $_[0]; |
|
462
|
|
|
|
|
|
|
} |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
# Given an encoded element returns a string version. Should be suitable for use as a key in a hash as well as |
|
465
|
|
|
|
|
|
|
# being invertible with destringify. |
|
466
|
|
|
|
|
|
|
sub stringify { |
|
467
|
7
|
|
|
7
|
1
|
3105
|
return &{$stringifier}; |
|
|
7
|
|
|
|
|
98
|
|
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
} |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
{ |
|
472
|
|
|
|
|
|
|
my $destringifier = \&_default_destringifier; |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
sub destringify_by { |
|
475
|
1
|
|
|
1
|
1
|
584
|
$destringifier = $_[0]; |
|
476
|
|
|
|
|
|
|
} |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub destringify { |
|
479
|
6
|
|
|
6
|
1
|
395
|
return &{$destringifier}; |
|
|
6
|
|
|
|
|
25
|
|
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
} |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
{ |
|
484
|
|
|
|
|
|
|
my $monitors = {}; |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
sub monitor_args_by { |
|
487
|
1
|
|
|
1
|
1
|
4
|
$monitors = $_[0]; |
|
488
|
|
|
|
|
|
|
} |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
# aliases act like references, but look like simple scalars. Because of this we have to be particularly |
|
491
|
|
|
|
|
|
|
# cautious where they could appear. Barring XS code and the sub{\@_} construction we only need to worry |
|
492
|
|
|
|
|
|
|
# about subroutine arguments, i.e. $_[i]. |
|
493
|
|
|
|
|
|
|
# |
|
494
|
|
|
|
|
|
|
# Accepts a reference to an array of aliases, |
|
495
|
|
|
|
|
|
|
# e.g. @_ from another subroutine. It will monitor each alias that is not read-only and return a tuple |
|
496
|
|
|
|
|
|
|
# consisting of the total number of aliases from the array reference as well as a hash reference that takes |
|
497
|
|
|
|
|
|
|
# an index of a mutable element in the array to the result of monitor being called on a reference to said |
|
498
|
|
|
|
|
|
|
# element. |
|
499
|
|
|
|
|
|
|
sub monitor_args { |
|
500
|
4
|
|
|
4
|
1
|
1880
|
my ( $package, $subroutine, $aliases ) = @_; |
|
501
|
|
|
|
|
|
|
|
|
502
|
4
|
|
|
|
|
5
|
my $arg_monitor; |
|
503
|
4
|
100
|
100
|
|
|
57
|
if ( defined( $arg_monitor = $monitors->{'packages'}->{$package}->{'subs'}->{$subroutine}->{'monitor_args'} ) |
|
|
|
|
100
|
|
|
|
|
|
504
|
|
|
|
|
|
|
|| defined( $arg_monitor = $monitors->{'packages'}->{$package}->{'monitor_args'} ) |
|
505
|
|
|
|
|
|
|
|| defined( $arg_monitor = $monitors->{'monitor_args'} ) ) { |
|
506
|
|
|
|
|
|
|
|
|
507
|
3
|
|
|
|
|
5
|
return &{$arg_monitor}($aliases); |
|
|
3
|
|
|
|
|
8
|
|
|
508
|
|
|
|
|
|
|
} |
|
509
|
|
|
|
|
|
|
else { |
|
510
|
1
|
|
|
|
|
3
|
my $num_aliases = @{$aliases}; |
|
|
1
|
|
|
|
|
2
|
|
|
511
|
1
|
|
|
|
|
3
|
my %mutable; |
|
512
|
1
|
|
|
|
|
8
|
for ( my $i = 0; $i < $num_aliases; $i++ ) { |
|
513
|
2
|
50
|
|
|
|
28
|
if ( ! readonly( $aliases->[$i] ) ) { |
|
514
|
2
|
|
|
|
|
8
|
$mutable{$i} = monitor( \$aliases->[$i] ); |
|
515
|
|
|
|
|
|
|
} |
|
516
|
|
|
|
|
|
|
} |
|
517
|
1
|
|
|
|
|
11
|
return [ $num_aliases, \%mutable ]; |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
} |
|
520
|
|
|
|
|
|
|
} |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
{ |
|
523
|
|
|
|
|
|
|
my $players = {}; |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub play_args_by { |
|
526
|
1
|
|
|
1
|
1
|
502
|
$players = $_[0]; |
|
527
|
|
|
|
|
|
|
} |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# Accepts an array of aliases and the tuple returned by monitor_args. |
|
530
|
|
|
|
|
|
|
# Attempts to match the aliases in the array reference with those in the tuple. If everything matches the |
|
531
|
|
|
|
|
|
|
# mutable passed aliases will be tied to behave as those monitored earlier, otherwise dies. The array and |
|
532
|
|
|
|
|
|
|
# the tuple representing the original array are said to match if the total number of elements are the same |
|
533
|
|
|
|
|
|
|
# and the mutable elements are the same, i.e. appear at the same indices. |
|
534
|
|
|
|
|
|
|
sub play_args { |
|
535
|
3
|
|
|
3
|
1
|
1365
|
my ( $package, $subroutine, $aliases, $coded_aliases ) = @_; |
|
536
|
|
|
|
|
|
|
|
|
537
|
3
|
|
|
|
|
5
|
my $arg_player; |
|
538
|
3
|
50
|
100
|
|
|
35
|
if ( defined( $arg_player = $players->{'packages'}->{$package}->{'subs'}->{$subroutine}->{'play_args'} ) |
|
|
|
|
66
|
|
|
|
|
|
539
|
|
|
|
|
|
|
|| defined( $arg_player = $players->{'packages'}->{$package}->{'play_args'} ) |
|
540
|
|
|
|
|
|
|
|| defined( $arg_player = $players->{'play_args'} ) ) { |
|
541
|
|
|
|
|
|
|
|
|
542
|
3
|
|
|
|
|
3
|
&{$arg_player}( $aliases, $coded_aliases ); |
|
|
3
|
|
|
|
|
9
|
|
|
543
|
|
|
|
|
|
|
} |
|
544
|
|
|
|
|
|
|
else { |
|
545
|
0
|
|
|
|
|
0
|
my ( $orig_num_aliases, $mutable ) = @{$coded_aliases}; |
|
|
0
|
|
|
|
|
0
|
|
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
# Apply a primitive signature check, list length. |
|
548
|
0
|
|
|
|
|
0
|
my $cur_num_aliases = @{$aliases}; |
|
|
0
|
|
|
|
|
0
|
|
|
549
|
0
|
0
|
|
|
|
0
|
if ( $orig_num_aliases != $cur_num_aliases ) { |
|
550
|
0
|
|
|
|
|
0
|
die "Signatures do not match. Unable to play_args from <$coded_aliases> onto <$aliases>."; |
|
551
|
|
|
|
|
|
|
} |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
# Consider each alias, tie the mutable aliases if everything matches, else die. |
|
554
|
0
|
|
|
|
|
0
|
for ( my $i = 0; $i < $cur_num_aliases; $i++ ) { |
|
555
|
0
|
|
|
|
|
0
|
my $cur_read_only = readonly( $aliases->[$i] ); |
|
556
|
0
|
|
|
|
|
0
|
my $orig_read_only = ! exists( $mutable->{$i} ); |
|
557
|
|
|
|
|
|
|
|
|
558
|
0
|
0
|
0
|
|
|
0
|
if ( $cur_read_only && $orig_read_only ) { # If they are both read-only they match. |
|
|
|
0
|
0
|
|
|
|
|
|
559
|
0
|
|
|
|
|
0
|
next; # We shouldn't try to tie a read-only variable. :) |
|
560
|
|
|
|
|
|
|
} |
|
561
|
|
|
|
|
|
|
elsif ( ! $cur_read_only && ! $orig_read_only ) { # If they are both mutable... |
|
562
|
0
|
|
|
|
|
0
|
my $index = $mutable->{$i}->[DATA]; # See monitor. |
|
563
|
|
|
|
|
|
|
|
|
564
|
0
|
0
|
|
|
|
0
|
if ( defined( $index_to_reference->{$index} ) ) { # If we have already seen this value. |
|
565
|
0
|
|
|
|
|
0
|
next; |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
#TODO: Assuming we maintain address_to_index and is_alive during playback too we can |
|
569
|
|
|
|
|
|
|
# check to see if $address_to_index{ refaddr( $index_to_reference{$index} ) } == $index. |
|
570
|
|
|
|
|
|
|
# If it doesn't we know that there is a problem. <---- that like something Or. |
|
571
|
|
|
|
|
|
|
|
|
572
|
0
|
|
|
|
|
0
|
my ( $type, $history, $old_class ) = @{ $references->[$index] }; |
|
|
0
|
|
|
|
|
0
|
|
|
573
|
0
|
|
|
|
|
0
|
tie( $aliases->[$i], 'Test::Mimic::Library::PlayScalar', $history ); |
|
574
|
0
|
|
|
|
|
0
|
$index_to_reference->{$index} = \( $aliases->[$i] ); |
|
575
|
0
|
|
|
|
|
0
|
weaken( $index_to_reference->{$index} ); # Don't prevent the val from being gced. |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
#NOTE: We need not bless the alias here. Either we produced it earlier, blessed it then and hit |
|
578
|
|
|
|
|
|
|
# next above or the alias was produced externally and if blessed at all was blessed |
|
579
|
|
|
|
|
|
|
# elsewhere. |
|
580
|
|
|
|
|
|
|
|
|
581
|
0
|
|
|
|
|
0
|
my $address = refaddr( \( $aliases->[$i] ) ); |
|
582
|
0
|
|
|
|
|
0
|
$address_to_index->{$address} = $index; |
|
583
|
0
|
|
|
|
|
0
|
$is_alive->{$address} = \( $aliases->[$i] ); |
|
584
|
0
|
|
|
|
|
0
|
weaken( $is_alive->{$address} ); |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
} |
|
587
|
|
|
|
|
|
|
else { |
|
588
|
0
|
|
|
|
|
0
|
die "Mutable/immutable mismatch. Unable to play_args from <$coded_aliases> onto " |
|
589
|
|
|
|
|
|
|
. "<$aliases>."; |
|
590
|
|
|
|
|
|
|
} |
|
591
|
|
|
|
|
|
|
} |
|
592
|
|
|
|
|
|
|
} |
|
593
|
|
|
|
|
|
|
} |
|
594
|
|
|
|
|
|
|
} |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
sub _get_type { |
|
597
|
0
|
|
|
0
|
|
0
|
my ($val) = @_; |
|
598
|
|
|
|
|
|
|
|
|
599
|
0
|
0
|
|
|
|
0
|
if ( _is_pattern($val) ) { |
|
600
|
0
|
|
|
|
|
0
|
return 'REG_EXP'; |
|
601
|
|
|
|
|
|
|
} |
|
602
|
|
|
|
|
|
|
else { |
|
603
|
0
|
|
|
|
|
0
|
my $type = reftype($val); |
|
604
|
0
|
0
|
0
|
|
|
0
|
if ( $type eq 'REF' || $type eq 'LVALUE' || $type eq 'VSTRING' ) { |
|
|
|
|
0
|
|
|
|
|
|
605
|
0
|
|
|
|
|
0
|
return 'SCALAR'; |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
else { |
|
608
|
0
|
|
|
|
|
0
|
return $type; |
|
609
|
|
|
|
|
|
|
} |
|
610
|
|
|
|
|
|
|
} |
|
611
|
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
{ |
|
614
|
|
|
|
|
|
|
# Each of these helper subroutines takes ( $val, $type ). |
|
615
|
|
|
|
|
|
|
my $scalar_action = sub { |
|
616
|
|
|
|
|
|
|
my $history = []; |
|
617
|
|
|
|
|
|
|
if ( defined( my $old_tie = tied( ${ $_[0] } ) ) ) { |
|
618
|
|
|
|
|
|
|
tie( ${ $_[0] }, 'Test::Mimic::Library::MonitorTiedScalar', $history, $old_tie ); |
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
else { |
|
621
|
|
|
|
|
|
|
tie( ${ $_[0] }, 'Test::Mimic::Library::MonitorScalar', $history, $_[0] ); |
|
622
|
|
|
|
|
|
|
} |
|
623
|
|
|
|
|
|
|
return [ 'SCALAR', $history ]; |
|
624
|
|
|
|
|
|
|
}; |
|
625
|
|
|
|
|
|
|
my $simple_action = sub { return [ $_[1], $_[0] ]; }; |
|
626
|
|
|
|
|
|
|
my %type_to_action = ( |
|
627
|
|
|
|
|
|
|
'REG_EXP' => $simple_action, |
|
628
|
|
|
|
|
|
|
'SCALAR' => $scalar_action, |
|
629
|
|
|
|
|
|
|
'REF' => $scalar_action, |
|
630
|
|
|
|
|
|
|
'LVALUE' => $scalar_action, |
|
631
|
|
|
|
|
|
|
'VSTRING' => $scalar_action, |
|
632
|
|
|
|
|
|
|
'ARRAY' => sub { |
|
633
|
|
|
|
|
|
|
my $history = []; |
|
634
|
|
|
|
|
|
|
if ( defined( my $old_tie = tied( @{ $_[0] } ) ) ) { |
|
635
|
|
|
|
|
|
|
tie( @{ $_[0] }, 'Test::Mimic::Library::MonitorTiedArray', $history, $old_tie ); |
|
636
|
|
|
|
|
|
|
} |
|
637
|
|
|
|
|
|
|
else { |
|
638
|
|
|
|
|
|
|
tie ( @{ $_[0] }, 'Test::Mimic::Library::MonitorArray', $history, $_[0] ); |
|
639
|
|
|
|
|
|
|
} |
|
640
|
|
|
|
|
|
|
return [ 'ARRAY', $history ]; |
|
641
|
|
|
|
|
|
|
}, |
|
642
|
|
|
|
|
|
|
'HASH' => sub { |
|
643
|
|
|
|
|
|
|
my $history = []; |
|
644
|
|
|
|
|
|
|
if ( defined( my $old_tie = tied( %{ $_[0] } ) ) ) { |
|
645
|
|
|
|
|
|
|
tie( %{ $_[0] }, 'Test::Mimic::Library::MonitorTiedHash', $history, $old_tie ); |
|
646
|
|
|
|
|
|
|
} |
|
647
|
|
|
|
|
|
|
else { |
|
648
|
|
|
|
|
|
|
tie ( %{ $_[0] }, 'Test::Mimic::Library::MonitorHash', $history, $_[0] ); |
|
649
|
|
|
|
|
|
|
} |
|
650
|
|
|
|
|
|
|
return [ 'HASH', $history ]; |
|
651
|
|
|
|
|
|
|
}, |
|
652
|
|
|
|
|
|
|
'GLOB' => $simple_action, |
|
653
|
|
|
|
|
|
|
'IO' => $simple_action, |
|
654
|
|
|
|
|
|
|
'FORMAT' => $simple_action, |
|
655
|
|
|
|
|
|
|
'CODE' => $simple_action, |
|
656
|
|
|
|
|
|
|
); |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# Monitor, i.e. tie the value and record its state, if possible (recursively as needed), otherwise merely |
|
659
|
|
|
|
|
|
|
# encapsulate the value as well as possible. In the second case proper storage and retrivial of the data |
|
660
|
|
|
|
|
|
|
# becomes the responsibility of Test::Mimic::Recorder::stringify. |
|
661
|
|
|
|
|
|
|
# |
|
662
|
|
|
|
|
|
|
# Objects are handled, but to a limited extent. The main restriction is that a reference (or rather the |
|
663
|
|
|
|
|
|
|
# 'object' behind the reference) can not change from being blessed to being unblessed anywhere that monitor |
|
664
|
|
|
|
|
|
|
# will notice. Purely internal modifications, i.e. those occurring in a wrapped subroutine, are okay. |
|
665
|
|
|
|
|
|
|
# Additionally, modifications occurring prior to the reference being monitored are okay. Also, it should be |
|
666
|
|
|
|
|
|
|
# noted that references blessed into a package that is not being recorded will have their state recorded |
|
667
|
|
|
|
|
|
|
# properly (including object info), but that object method calls on that reference will still not be |
|
668
|
|
|
|
|
|
|
# recorded. |
|
669
|
|
|
|
|
|
|
sub monitor { |
|
670
|
2
|
|
|
2
|
1
|
4
|
my ( $val ) = @_; |
|
671
|
|
|
|
|
|
|
|
|
672
|
2
|
|
|
|
|
7
|
my $type = reftype($val); |
|
673
|
2
|
50
|
|
|
|
7
|
if ( ! $type ) { # If this is not a reference... |
|
674
|
0
|
|
|
|
|
0
|
return [ STABLE, $val ]; |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
else { |
|
677
|
2
|
|
|
|
|
6
|
my $address = refaddr($val); |
|
678
|
2
|
|
|
|
|
4
|
my $index; |
|
679
|
|
|
|
|
|
|
|
|
680
|
2
|
50
|
|
|
|
7
|
if ( defined( $is_alive->{$address} ) ) { # If we are watching this reference... |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
# NOTE: We are using defined as opposed to exists because a given address can be used by multiple |
|
683
|
|
|
|
|
|
|
# references over the entire execution of the program. See the comment on weaken below. |
|
684
|
|
|
|
|
|
|
|
|
685
|
0
|
|
|
|
|
0
|
$index = $address_to_index->{$address}; |
|
686
|
|
|
|
|
|
|
} |
|
687
|
|
|
|
|
|
|
else { |
|
688
|
|
|
|
|
|
|
# Note that we are watching the reference. |
|
689
|
2
|
|
|
|
|
6
|
$is_alive->{$address} = $val; |
|
690
|
2
|
|
|
|
|
6
|
weaken( $is_alive->{$address} ); # This reference will be automatically set to undef when $$val is |
|
691
|
|
|
|
|
|
|
# garbage collected. |
|
692
|
|
|
|
|
|
|
|
|
693
|
2
|
50
|
|
|
|
6
|
if ( _is_pattern($val) ) { # reftype doesn't recognize patterns, so set $type manually. |
|
694
|
0
|
|
|
|
|
0
|
$type = 'REG_EXP'; |
|
695
|
|
|
|
|
|
|
} |
|
696
|
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
# Create a representation of the reference depending on its type. |
|
698
|
|
|
|
|
|
|
# Monitors recursively as necessary. |
|
699
|
2
|
|
|
|
|
4
|
my $reference; |
|
700
|
2
|
50
|
|
|
|
6
|
if ( exists( $type_to_action{$type} ) ) { |
|
701
|
2
|
|
|
|
|
4
|
$reference = &{ $type_to_action{$type} }( $val, $type ); |
|
|
2
|
|
|
|
|
8
|
|
|
702
|
|
|
|
|
|
|
} |
|
703
|
|
|
|
|
|
|
else { |
|
704
|
0
|
|
|
|
|
0
|
die "Unknown reference type <$type> from <$val>. Unable to monitor."; |
|
705
|
|
|
|
|
|
|
} |
|
706
|
2
|
|
|
|
|
6
|
$reference->[2] = blessed($val); # Mark this as either an object or a plain reference. |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
# Store the representation of the reference into the references table. |
|
709
|
2
|
|
|
|
|
4
|
push( @{$references}, $reference ); |
|
|
2
|
|
|
|
|
4
|
|
|
710
|
2
|
|
|
|
|
11
|
$index = $address_to_index->{$address} = $#{$references}; |
|
|
2
|
|
|
|
|
7
|
|
|
711
|
|
|
|
|
|
|
} |
|
712
|
2
|
|
|
|
|
20
|
return [ VOLATILE, $index ]; |
|
713
|
|
|
|
|
|
|
} |
|
714
|
|
|
|
|
|
|
} |
|
715
|
|
|
|
|
|
|
} |
|
716
|
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
{ |
|
718
|
|
|
|
|
|
|
# Each of these helper subroutines takes ( $val, $at_level, $type ). |
|
719
|
|
|
|
|
|
|
my $scalar_action = sub { return [ 'SCALAR', encode( ${ $_[0] }, $_[1] ) ]; }; |
|
720
|
|
|
|
|
|
|
my $simple_action = sub { return [ $_[2], $_[0] ]; }; |
|
721
|
|
|
|
|
|
|
my %type_to_action = ( |
|
722
|
|
|
|
|
|
|
'REG_EXP' => $simple_action, |
|
723
|
|
|
|
|
|
|
'SCALAR' => $scalar_action, |
|
724
|
|
|
|
|
|
|
'REF' => $scalar_action, |
|
725
|
|
|
|
|
|
|
'LVALUE' => $scalar_action, |
|
726
|
|
|
|
|
|
|
'VSTRING' => $scalar_action, |
|
727
|
|
|
|
|
|
|
'ARRAY' => sub { |
|
728
|
|
|
|
|
|
|
my @temp = map( { encode( $_, $_[1] ) } @{ $_[0] } ); |
|
729
|
|
|
|
|
|
|
return [ 'ARRAY', \@temp ]; |
|
730
|
|
|
|
|
|
|
}, |
|
731
|
|
|
|
|
|
|
'HASH' => sub { |
|
732
|
|
|
|
|
|
|
my %temp; |
|
733
|
|
|
|
|
|
|
@temp{ keys %{ $_[0] } } = map( { encode( $_[0]->{$_}, $_[1] ) } keys %{ $_[0] } ); |
|
734
|
|
|
|
|
|
|
return [ 'HASH', \%temp]; |
|
735
|
|
|
|
|
|
|
}, |
|
736
|
|
|
|
|
|
|
'GLOB' => $simple_action, |
|
737
|
|
|
|
|
|
|
'IO' => $simple_action, |
|
738
|
|
|
|
|
|
|
'FORMAT' => $simple_action, |
|
739
|
|
|
|
|
|
|
'CODE' => $simple_action, |
|
740
|
|
|
|
|
|
|
); |
|
741
|
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
# Performs an expansion wrap on the passed value until the given level then watches every component below. |
|
743
|
|
|
|
|
|
|
# Returns a structure analogous to the original except that each component is recursively wrapped. This should |
|
744
|
|
|
|
|
|
|
# only be used on static data. If circular references exist above the watch level or into the wrap level the |
|
745
|
|
|
|
|
|
|
# behavior is undefined. |
|
746
|
|
|
|
|
|
|
# |
|
747
|
|
|
|
|
|
|
# For example if _watch was passed an array it would perhaps return [ VOLATILE, 453 ]. |
|
748
|
|
|
|
|
|
|
# _wrap_then_watch would return [ NESTED, [ ARRAY, [ [ STABLE, 'foo' ], [ STABLE, 'bar' ] ] ] ] |
|
749
|
|
|
|
|
|
|
# |
|
750
|
|
|
|
|
|
|
# This is useful when the data currently in the array is important, but the array itself has no special |
|
751
|
|
|
|
|
|
|
# significance. |
|
752
|
|
|
|
|
|
|
# |
|
753
|
|
|
|
|
|
|
# Currently scalars et al., arrays, hashes, qr objects, code references are handled well. |
|
754
|
|
|
|
|
|
|
# Filehandles are not being tied, ideally they would be, but the filehandle tying mechanism is |
|
755
|
|
|
|
|
|
|
# not complete. |
|
756
|
|
|
|
|
|
|
# Formats are in a similar position, but they probably shouldn't ever be redefined. (Check this.) |
|
757
|
|
|
|
|
|
|
# Because of this that may not really be a problem. |
|
758
|
|
|
|
|
|
|
# The entries in globs can not be tied. A special glob tie could potentially remedy this, but |
|
759
|
|
|
|
|
|
|
# this does not currently exist. |
|
760
|
|
|
|
|
|
|
# |
|
761
|
|
|
|
|
|
|
# TODO: Handle circular references, also save space on DAGs. |
|
762
|
|
|
|
|
|
|
# Idea: Scan through structure. Record all references in a big hash. If we see duplicates note them. |
|
763
|
|
|
|
|
|
|
# The duplicates will exist as a special structure. |
|
764
|
|
|
|
|
|
|
# |
|
765
|
|
|
|
|
|
|
# [ CIRCULAR_NESTED, , [ ARRAY, blah... |
|
766
|
|
|
|
|
|
|
# We have one additional type: |
|
767
|
|
|
|
|
|
|
# [ DUP, ] |
|
768
|
|
|
|
|
|
|
sub encode { |
|
769
|
0
|
|
|
0
|
1
|
|
my ( $val, $at_level ) = @_; |
|
770
|
|
|
|
|
|
|
|
|
771
|
0
|
0
|
|
|
|
|
if ( $at_level == 0 ) { # If we have reached the volatile layer... |
|
772
|
0
|
|
|
|
|
|
return monitor( $val ); |
|
773
|
|
|
|
|
|
|
} |
|
774
|
|
|
|
|
|
|
else { |
|
775
|
0
|
|
|
|
|
|
$at_level--; |
|
776
|
|
|
|
|
|
|
} |
|
777
|
|
|
|
|
|
|
|
|
778
|
0
|
|
|
|
|
|
my $type = reftype($val); |
|
779
|
0
|
0
|
|
|
|
|
if ( ! $type ) { # If the value is not a reference... |
|
|
|
0
|
|
|
|
|
|
|
780
|
0
|
|
|
|
|
|
return [ STABLE, $val ]; |
|
781
|
|
|
|
|
|
|
} |
|
782
|
|
|
|
|
|
|
elsif ( exists( $type_to_action{$type} ) ) { |
|
783
|
0
|
0
|
|
|
|
|
if ( _is_pattern($val) ) { # reftype doesn't recognize patterns, so set $type manually. |
|
784
|
0
|
|
|
|
|
|
$type = 'REG_EXP'; |
|
785
|
|
|
|
|
|
|
} |
|
786
|
0
|
|
|
|
|
|
my $coded = &{ $type_to_action{$type} }( $val, $at_level, $type ); |
|
|
0
|
|
|
|
|
|
|
|
787
|
0
|
|
|
|
|
|
return [ NESTED, $coded ]; |
|
788
|
|
|
|
|
|
|
} |
|
789
|
|
|
|
|
|
|
else { |
|
790
|
0
|
|
|
|
|
|
die "Unknown reference type <$type> from <$val>. Unable to encode."; |
|
791
|
|
|
|
|
|
|
} |
|
792
|
|
|
|
|
|
|
} |
|
793
|
|
|
|
|
|
|
} |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
{ |
|
796
|
|
|
|
|
|
|
# Each of these helper subroutines takes ( $val ). |
|
797
|
|
|
|
|
|
|
my $simple_action = sub { return $_[0]; }; |
|
798
|
|
|
|
|
|
|
my %type_to_action = ( |
|
799
|
|
|
|
|
|
|
'REG_EXP' => $simple_action, |
|
800
|
|
|
|
|
|
|
'SCALAR' => sub { |
|
801
|
|
|
|
|
|
|
my $temp = decode( $_[0] ); |
|
802
|
|
|
|
|
|
|
return \$temp; |
|
803
|
|
|
|
|
|
|
}, |
|
804
|
|
|
|
|
|
|
'ARRAY' => sub { |
|
805
|
|
|
|
|
|
|
my @temp = map( { decode( $_ ) } @{ $_[0] } ); |
|
806
|
|
|
|
|
|
|
return \@temp; |
|
807
|
|
|
|
|
|
|
}, |
|
808
|
|
|
|
|
|
|
'HASH' => sub { |
|
809
|
|
|
|
|
|
|
my %temp; |
|
810
|
|
|
|
|
|
|
@temp{ keys %{ $_[0] } } = map( { decode( $_[0]->[$_] ) } keys %{ $_[0] } ); |
|
811
|
|
|
|
|
|
|
return \%temp; |
|
812
|
|
|
|
|
|
|
}, |
|
813
|
|
|
|
|
|
|
'GLOB' => $simple_action, |
|
814
|
|
|
|
|
|
|
'IO' => $simple_action, |
|
815
|
|
|
|
|
|
|
'FORMAT' => $simple_action, |
|
816
|
|
|
|
|
|
|
'CODE' => $simple_action, |
|
817
|
|
|
|
|
|
|
); |
|
818
|
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
sub decode { |
|
820
|
0
|
|
|
0
|
1
|
|
my ( $coded_val ) = @_; |
|
821
|
0
|
|
|
|
|
|
my ( $code_type, $data ) = @{$coded_val}; |
|
|
0
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
|
|
823
|
0
|
0
|
|
|
|
|
if ( $code_type == STABLE ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
824
|
0
|
|
|
|
|
|
return $data; |
|
825
|
|
|
|
|
|
|
} |
|
826
|
|
|
|
|
|
|
elsif ( $code_type == NESTED ) { |
|
827
|
0
|
|
|
|
|
|
my ( $ref_type, $val ) = @{$data}; |
|
|
0
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
|
|
829
|
0
|
0
|
|
|
|
|
if ( exists( $type_to_action{$ref_type} ) ) { |
|
830
|
0
|
|
|
|
|
|
return &{ $type_to_action{$ref_type} }( $val ); |
|
|
0
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
} |
|
832
|
|
|
|
|
|
|
else { |
|
833
|
0
|
|
|
|
|
|
die "Invalid reference type <$ref_type> from <$data> with value <$val>. Unable to decode."; |
|
834
|
|
|
|
|
|
|
} |
|
835
|
|
|
|
|
|
|
} |
|
836
|
|
|
|
|
|
|
elsif ( $code_type == VOLATILE ) { |
|
837
|
0
|
|
|
|
|
|
return play( $coded_val ); |
|
838
|
|
|
|
|
|
|
} |
|
839
|
|
|
|
|
|
|
else { |
|
840
|
0
|
|
|
|
|
|
die "Invalid code type <$code_type> from <$coded_val> with data <$data>. Unable to decode."; |
|
841
|
|
|
|
|
|
|
} |
|
842
|
|
|
|
|
|
|
} |
|
843
|
|
|
|
|
|
|
} |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
{ |
|
846
|
|
|
|
|
|
|
# Each of these helper subroutines takes ( $history ). |
|
847
|
|
|
|
|
|
|
# This will be a single reference, i.e. not a true history, for types we do not tie. |
|
848
|
|
|
|
|
|
|
my $simple_action = sub { return $_[0]; }; |
|
849
|
|
|
|
|
|
|
my %type_to_action = ( |
|
850
|
|
|
|
|
|
|
'REG_EXP' => $simple_action, |
|
851
|
|
|
|
|
|
|
'SCALAR' => sub { |
|
852
|
|
|
|
|
|
|
my $temp; |
|
853
|
|
|
|
|
|
|
tie( $temp, 'Test::Mimic::Library::PlayScalar', $_[0] ); |
|
854
|
|
|
|
|
|
|
return \$temp; |
|
855
|
|
|
|
|
|
|
}, |
|
856
|
|
|
|
|
|
|
'ARRAY' => sub { |
|
857
|
|
|
|
|
|
|
my @temp; |
|
858
|
|
|
|
|
|
|
tie( @temp, 'Test::Mimic::Library::PlayArray', $_[0] ); |
|
859
|
|
|
|
|
|
|
return \@temp; |
|
860
|
|
|
|
|
|
|
}, |
|
861
|
|
|
|
|
|
|
'HASH' => sub { |
|
862
|
|
|
|
|
|
|
my %temp; |
|
863
|
|
|
|
|
|
|
tie( %temp, 'Test::Mimic::Library::PlayHash', $_[0] ); |
|
864
|
|
|
|
|
|
|
return \%temp; |
|
865
|
|
|
|
|
|
|
}, |
|
866
|
|
|
|
|
|
|
'GLOB' => $simple_action, |
|
867
|
|
|
|
|
|
|
'IO' => $simple_action, |
|
868
|
|
|
|
|
|
|
'FORMAT' => $simple_action, |
|
869
|
|
|
|
|
|
|
'CODE' => $simple_action, |
|
870
|
|
|
|
|
|
|
); |
|
871
|
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
sub play { |
|
873
|
0
|
|
|
0
|
1
|
|
my ( $coded_val ) = @_; |
|
874
|
|
|
|
|
|
|
|
|
875
|
0
|
|
|
|
|
|
my ( $type, $data ) = @{$coded_val}; |
|
|
0
|
|
|
|
|
|
|
|
876
|
0
|
0
|
|
|
|
|
if ( $type == STABLE ) { |
|
|
|
0
|
|
|
|
|
|
|
877
|
0
|
|
|
|
|
|
return $data; |
|
878
|
|
|
|
|
|
|
} |
|
879
|
|
|
|
|
|
|
elsif ( $type == VOLATILE ) { |
|
880
|
0
|
0
|
|
|
|
|
if ( defined( $index_to_reference->{$data} ) ) { # We are using defined because the weak |
|
881
|
|
|
|
|
|
|
# references used in the hash will be set to |
|
882
|
|
|
|
|
|
|
# undef upon the destruction of the |
|
883
|
|
|
|
|
|
|
# corresponding values. |
|
884
|
0
|
|
|
|
|
|
return $index_to_reference->{$data}; |
|
885
|
|
|
|
|
|
|
} |
|
886
|
|
|
|
|
|
|
else { |
|
887
|
0
|
|
|
|
|
|
my ( $type, $history, $class_name ) = @{ $references->[$data] }; |
|
|
0
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
|
|
889
|
0
|
|
|
|
|
|
my $reference; |
|
890
|
0
|
0
|
|
|
|
|
if ( exists( $type_to_action{$type} ) ) { |
|
891
|
0
|
|
|
|
|
|
$reference = &{ $type_to_action{$type} }( $history ); |
|
|
0
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
} |
|
893
|
|
|
|
|
|
|
else { |
|
894
|
0
|
|
|
|
|
|
die "Unknown reference type <$type> at index <$data>. Unable to play."; |
|
895
|
|
|
|
|
|
|
} |
|
896
|
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
# If this reference is supposed to point at an object, bless it. |
|
898
|
|
|
|
|
|
|
# This will take place even if we didn't record the class. This may be a feature or a bug. |
|
899
|
0
|
0
|
|
|
|
|
if ( defined($class_name) ) { |
|
900
|
0
|
|
|
|
|
|
bless( $reference, $class_name ); |
|
901
|
|
|
|
|
|
|
} |
|
902
|
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
# Note the creation of this reference, so we don't recreate it and are aware of what recorded |
|
904
|
|
|
|
|
|
|
# reference it corresponds to. |
|
905
|
0
|
|
|
|
|
|
my $address = refaddr($reference); |
|
906
|
0
|
|
|
|
|
|
$address_to_index->{$address} = $data; |
|
907
|
0
|
|
|
|
|
|
$is_alive->{$address} = $reference; |
|
908
|
0
|
|
|
|
|
|
weaken( $is_alive->{$address} ); |
|
909
|
0
|
|
|
|
|
|
$index_to_reference->{$data} = $reference; |
|
910
|
0
|
|
|
|
|
|
weaken( $index_to_reference->{$data} ); # But don't prevent it from being gced. If we |
|
911
|
|
|
|
|
|
|
# need to we can recreate it easily. ( Although the |
|
912
|
|
|
|
|
|
|
# address may well be different. ) |
|
913
|
|
|
|
|
|
|
|
|
914
|
0
|
|
|
|
|
|
return $reference; |
|
915
|
|
|
|
|
|
|
} |
|
916
|
|
|
|
|
|
|
} |
|
917
|
|
|
|
|
|
|
else { |
|
918
|
0
|
|
|
|
|
|
die "Unrecognized type <$type>. Unable to play."; |
|
919
|
|
|
|
|
|
|
} |
|
920
|
|
|
|
|
|
|
} |
|
921
|
|
|
|
|
|
|
} |
|
922
|
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
1; |
|
924
|
|
|
|
|
|
|
__END__ |