1 /*
2 Copyright (C) 2001-2014, Parrot Foundation.
3 
4 =head1 NAME
5 
6 src/pmc_freeze.c - Freeze and thaw functionality
7 
8 =head1 DESCRIPTION
9 
10 Thawing PMCs uses a list with (maximum) size of the amount of PMCs to
11 keep track of retrieved PMCs.
12 
13 PMCs freeze and thaw their own information through their vtables.
14 
15 To avoid recursion, the whole functionality is driven by
16 C<< pmc->vtable->visit >>, which is called for the first PMC initially.
17 Container PMCs call a "todo-callback" for all contained PMCs. The
18 individual action vtable (freeze/thaw) is then called for all todo-PMCs.
19 
20 =cut
21 
22 */
23 
24 #include "parrot/parrot.h"
25 #include "pmc/pmc_callcontext.h"
26 #include "object_serialization.str"
27 
28 /* when thawing a string longer then this size, we first do a GC run and then
29  * block GC - the system can't give us more headers */
30 
31 #define THAW_BLOCK_GC_SIZE 100000
32 
33 /* HEADERIZER HFILE: include/parrot/pmc_freeze.h */
34 
35 /*
36 
37 =head2 Public Interface
38 
39 =over 4
40 
41 =item C<STRING* Parrot_freeze(PARROT_INTERP, PMC *pmc)>
42 
43 Freeze using either method.
44 
45 =cut
46 
47 */
48 
49 PARROT_EXPORT
50 PARROT_WARN_UNUSED_RESULT
51 PARROT_CANNOT_RETURN_NULL
52 STRING*
Parrot_freeze(PARROT_INTERP,ARGIN (PMC * pmc))53 Parrot_freeze(PARROT_INTERP, ARGIN(PMC *pmc))
54 {
55     ASSERT_ARGS(Parrot_freeze)
56     PMC * const image = Parrot_pmc_new(interp, enum_class_ImageIOFreeze);
57     VTABLE_set_pmc(interp, image, pmc);
58     return VTABLE_get_string(interp, image);
59 }
60 
61 
62 /*
63 
64 =item C<opcode_t * Parrot_freeze_pbc(PARROT_INTERP, PMC *pmc, const
65 PackFile_ConstTable *pf, opcode_t *cursor, Hash **seen)>
66 
67 Freezes a PMC to a PackFile.
68 
69 =cut
70 
71 */
72 
73 PARROT_EXPORT
74 PARROT_WARN_UNUSED_RESULT
75 PARROT_CAN_RETURN_NULL
76 opcode_t *
Parrot_freeze_pbc(PARROT_INTERP,ARGIN (PMC * pmc),ARGIN (const PackFile_ConstTable * pf),ARGOUT (opcode_t * cursor),ARGOUT (Hash ** seen))77 Parrot_freeze_pbc(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(const PackFile_ConstTable *pf),
78     ARGOUT(opcode_t *cursor), ARGOUT(Hash **seen))
79 {
80     ASSERT_ARGS(Parrot_freeze_pbc)
81     PMC    *visitor;
82     STRING *image;
83     DECL_CONST_CAST;
84 
85     visitor  = Parrot_pmc_new(interp, enum_class_ImageIOFreeze);
86     VTABLE_set_pointer(interp, visitor,
87         PARROT_const_cast(void *, (const void *)pf));
88     VTABLE_set_pmc(interp, visitor, pmc);
89 
90     image  = VTABLE_get_string(interp, visitor);
91     *seen  = (Hash *)VTABLE_get_pointer(interp, visitor);
92     cursor = PF_store_buf(cursor, image);
93 
94     return cursor;
95 }
96 
97 
98 /*
99 
100 =item C<UINTVAL Parrot_freeze_pbc_size(PARROT_INTERP, PMC *pmc, const
101 PackFile_ConstTable *pf, Hash **seen)>
102 
103 Gets the size of an image if it were created using C<Parrot_freeze_pbc>.
104 
105 =cut
106 
107 */
108 
109 PARROT_EXPORT
110 PARROT_WARN_UNUSED_RESULT
111 UINTVAL
Parrot_freeze_pbc_size(PARROT_INTERP,ARGIN (PMC * pmc),ARGIN (const PackFile_ConstTable * pf),ARGOUT (Hash ** seen))112 Parrot_freeze_pbc_size(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(const PackFile_ConstTable *pf),
113         ARGOUT(Hash **seen))
114 {
115     ASSERT_ARGS(Parrot_freeze_pbc_size)
116     PMC * const pf_pmc = Parrot_pmc_new(interp, enum_class_UnManagedStruct);
117     PMC *visitor, *pmc_result;
118     DECL_CONST_CAST;
119 
120     VTABLE_set_pointer(interp, pf_pmc,
121         PARROT_const_cast(void *, (const void *)pf));
122 
123     visitor = Parrot_pmc_new_init(interp, enum_class_ImageIOSize, pf_pmc);
124     VTABLE_set_pmc(interp, visitor, pmc);
125 
126     pmc_result = VTABLE_get_pmc(interp, visitor);
127     *seen      = (Hash *)VTABLE_get_pointer(interp, visitor);
128     return VTABLE_get_integer(interp, pmc_result);
129 }
130 
131 
132 /*
133 
134 =item C<PMC * Parrot_freeze_strings(PARROT_INTERP, PMC *pmc)>
135 
136 Gets the strings of a PMC to be frozen.
137 
138 =cut
139 
140 */
141 
142 PARROT_EXPORT
143 PARROT_WARN_UNUSED_RESULT
144 PARROT_CANNOT_RETURN_NULL
145 PMC *
Parrot_freeze_strings(PARROT_INTERP,ARGIN (PMC * pmc))146 Parrot_freeze_strings(PARROT_INTERP, ARGIN(PMC *pmc))
147 {
148     ASSERT_ARGS(Parrot_freeze_strings)
149     PMC * const visitor = Parrot_pmc_new(interp, enum_class_ImageIOStrings);
150     VTABLE_set_pmc(interp, visitor, pmc);
151     return VTABLE_get_pmc(interp, visitor);
152 }
153 
154 /*
155 
156 =item C<void Parrot_pf_verify_image_string(PARROT_INTERP, STRING *image)>
157 
158 Perform some quick sanity checks on a packfile image string to verify that it
159 is valid. Throws exceptions if not.
160 
161 =cut
162 
163 */
164 
165 void
Parrot_pf_verify_image_string(PARROT_INTERP,ARGIN (STRING * image))166 Parrot_pf_verify_image_string(PARROT_INTERP, ARGIN(STRING *image))
167 {
168     ASSERT_ARGS(Parrot_pf_verify_image_string)
169     if (STRING_length(image) < 16)
170         Parrot_ex_throw_from_c_noargs(interp,
171             EXCEPTION_INVALID_STRING_REPRESENTATION,
172             "Cannot deserialize PMC: Incorrect Length");
173     else {
174         const char major = image->strstart[14];
175         const char minor = image->strstart[15];
176 
177         if (major == PARROT_PBC_MAJOR && minor == PARROT_PBC_MINOR)
178             return;
179 
180         Parrot_ex_throw_from_c_args(interp, NULL,
181             EXCEPTION_INVALID_STRING_REPRESENTATION,
182             "Version %d.%d of serialized PMC is invalid. Expected %d.%d. "
183             "You're probably linking against an incompatible libparrot.",
184             major, minor, PARROT_PBC_MAJOR, PARROT_PBC_MINOR);
185     }
186 }
187 
188 
189 /*
190 
191 =item C<PMC * Parrot_thaw(PARROT_INTERP, STRING *image)>
192 
193 Thaws a PMC.  Called from the C<thaw> opcode.
194 
195 For now it seems cheaper to use a list for remembering contained aggregates. We
196 could of course decide dynamically, which strategy to use: given a big image,
197 the first thawed item is a small aggregate. This implies it probably contains
198 more nested containers, for which another approach could be a win.
199 
200 =cut
201 
202 */
203 
204 PARROT_EXPORT
205 PARROT_WARN_UNUSED_RESULT
206 PARROT_CANNOT_RETURN_NULL
207 PMC *
Parrot_thaw(PARROT_INTERP,ARGIN (STRING * image))208 Parrot_thaw(PARROT_INTERP, ARGIN(STRING *image))
209 {
210     ASSERT_ARGS(Parrot_thaw)
211 
212     PMC        *result;
213     PMC * const info     = Parrot_pmc_new(interp, enum_class_ImageIOThaw);
214     int         gc_block = 0;
215 
216     /*
217      * if we are thawing a lot of PMCs, it's cheaper to do
218      * a GC run first and then block GC - the limit should be
219      * chosen so that no more then one GC run would be triggered
220      *
221      * XXX
222      *
223      * md5_3.pir shows a segfault during thawing the config hash
224      * info->thaw_ptr becomes invalid - seems that the hash got
225      * collected under us.
226      */
227     if (1 || (Parrot_str_byte_length(interp, image) > THAW_BLOCK_GC_SIZE)) {
228         Parrot_block_GC_mark(interp);
229         Parrot_block_GC_sweep(interp);
230         gc_block = 1;
231     }
232 
233     VTABLE_set_string_native(interp, info, image);
234     result = VTABLE_get_pmc(interp, info);
235 
236     if (gc_block) {
237         Parrot_unblock_GC_mark(interp);
238         Parrot_unblock_GC_sweep(interp);
239     }
240 
241     return result;
242 }
243 
244 
245 /*
246 
247 =item C<PMC* Parrot_thaw_pbc(PARROT_INTERP, PackFile_ConstTable *ct, const
248 opcode_t **cursor)>
249 
250 Thaw a pmc frozen by Parrot_freeze_pbc.
251 
252 =cut
253 
254 */
255 
256 PARROT_EXPORT
257 PARROT_WARN_UNUSED_RESULT
258 PARROT_CAN_RETURN_NULL
259 PMC*
Parrot_thaw_pbc(PARROT_INTERP,ARGIN (PackFile_ConstTable * ct),ARGMOD (const opcode_t ** cursor))260 Parrot_thaw_pbc(PARROT_INTERP, ARGIN(PackFile_ConstTable *ct), ARGMOD(const opcode_t **cursor))
261 {
262     ASSERT_ARGS(Parrot_thaw_pbc)
263     PackFile * const pf    = ct->base.pf;
264     STRING *   const image = PF_fetch_buf(interp, pf, cursor);
265     PMC *      const info  = Parrot_pmc_new(interp, enum_class_ImageIOThaw);
266     VTABLE_set_pointer(interp, info, ct);
267     VTABLE_set_string_native(interp, info, image);
268     return VTABLE_get_pmc(interp, info);
269 }
270 
271 
272 /*
273 
274 =item C<PMC* Parrot_thaw_constants(PARROT_INTERP, STRING *image)>
275 
276 This does nothing different from Parrot_thaw at the moment.
277 
278 =cut
279 
280 */
281 
282 PARROT_EXPORT
283 PARROT_WARN_UNUSED_RESULT
284 PARROT_CANNOT_RETURN_NULL
285 PMC*
Parrot_thaw_constants(PARROT_INTERP,ARGIN (STRING * image))286 Parrot_thaw_constants(PARROT_INTERP, ARGIN(STRING *image))
287 {
288     ASSERT_ARGS(Parrot_thaw_constants)
289     return Parrot_thaw(interp, image);
290 }
291 
292 
293 /*
294 
295 =item C<PMC* Parrot_clone(PARROT_INTERP, PMC *pmc)>
296 
297 There are for sure shortcuts to clone faster, e.g. always thaw the image
298 immediately or use a special callback.  For now we just thaw a frozen PMC.
299 
300 =cut
301 
302 */
303 
304 PARROT_EXPORT
305 PARROT_WARN_UNUSED_RESULT
306 PARROT_CAN_RETURN_NULL
307 PMC*
Parrot_clone(PARROT_INTERP,ARGIN (PMC * pmc))308 Parrot_clone(PARROT_INTERP, ARGIN(PMC *pmc))
309 {
310     ASSERT_ARGS(Parrot_clone)
311     return VTABLE_clone(interp, pmc);
312 }
313 
314 
315 /*
316 
317 =back
318 
319 =head1 TODO
320 
321 The seen-hash version for freezing might go away sometime.
322 
323 =head1 SEE ALSO
324 
325 Lot of discussion on p6i and F<docs/dev/pmc_freeze.pod>.
326 
327 =cut
328 
329 */
330 
331 
332 /*
333  * Local variables:
334  *   c-file-style: "parrot"
335  * End:
336  * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
337  */
338