2 |
- |
1 |
/* Copyright (C) 1989, 1995, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
|
|
|
2 |
|
|
|
3 |
This software is provided AS-IS with no warranty, either express or
|
|
|
4 |
implied.
|
|
|
5 |
|
|
|
6 |
This software is distributed under license and may not be copied,
|
|
|
7 |
modified or distributed except as expressly authorized under the terms
|
|
|
8 |
of the license contained in the file LICENSE in this distribution.
|
|
|
9 |
|
|
|
10 |
For more information about licensing, please refer to
|
|
|
11 |
http://www.ghostscript.com/licensing/. For information on
|
|
|
12 |
commercial licensing, go to http://www.artifex.com/licensing/ or
|
|
|
13 |
contact Artifex Software, Inc., 101 Lucas Valley Road #110,
|
|
|
14 |
San Rafael, CA 94903, U.S.A., +1(415)492-9861.
|
|
|
15 |
*/
|
|
|
16 |
|
|
|
17 |
/* $Id: zvmem.c,v 1.8 2004/08/04 19:36:13 stefan Exp $ */
|
|
|
18 |
/* "Virtual memory" operators */
|
|
|
19 |
#include "ghost.h"
|
|
|
20 |
#include "gsstruct.h"
|
|
|
21 |
#include "oper.h"
|
|
|
22 |
#include "estack.h" /* for checking in restore */
|
|
|
23 |
#include "ialloc.h"
|
|
|
24 |
#include "idict.h" /* ditto */
|
|
|
25 |
#include "igstate.h"
|
|
|
26 |
#include "isave.h"
|
|
|
27 |
#include "dstack.h"
|
|
|
28 |
#include "stream.h" /* for files.h */
|
|
|
29 |
#include "files.h" /* for e-stack processing */
|
|
|
30 |
#include "store.h"
|
|
|
31 |
#include "gsmatrix.h" /* for gsstate.h */
|
|
|
32 |
#include "gsstate.h"
|
|
|
33 |
|
|
|
34 |
/* Define whether we validate memory before/after save/restore. */
|
|
|
35 |
/* Note that we only actually do this if DEBUG is set and -Z? is selected. */
|
|
|
36 |
private const bool I_VALIDATE_BEFORE_SAVE = true;
|
|
|
37 |
private const bool I_VALIDATE_AFTER_SAVE = true;
|
|
|
38 |
private const bool I_VALIDATE_BEFORE_RESTORE = true;
|
|
|
39 |
private const bool I_VALIDATE_AFTER_RESTORE = true;
|
|
|
40 |
|
|
|
41 |
/* 'Save' structure */
|
|
|
42 |
typedef struct vm_save_s vm_save_t;
|
|
|
43 |
struct vm_save_s {
|
|
|
44 |
gs_state *gsave; /* old graphics state */
|
|
|
45 |
};
|
|
|
46 |
|
|
|
47 |
gs_private_st_ptrs1(st_vm_save, vm_save_t, "savetype",
|
|
|
48 |
vm_save_enum_ptrs, vm_save_reloc_ptrs, gsave);
|
|
|
49 |
|
|
|
50 |
/* Clean up the stacks and validate storage. */
|
|
|
51 |
private void
|
|
|
52 |
ivalidate_clean_spaces(i_ctx_t *i_ctx_p)
|
|
|
53 |
{
|
|
|
54 |
if (gs_debug_c('?')) {
|
|
|
55 |
ref_stack_cleanup(&d_stack);
|
|
|
56 |
ref_stack_cleanup(&e_stack);
|
|
|
57 |
ref_stack_cleanup(&o_stack);
|
|
|
58 |
ivalidate_spaces();
|
|
|
59 |
}
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
/* - save <save> */
|
|
|
63 |
int
|
|
|
64 |
zsave(i_ctx_t *i_ctx_p)
|
|
|
65 |
{
|
|
|
66 |
os_ptr op = osp;
|
|
|
67 |
uint space = icurrent_space;
|
|
|
68 |
vm_save_t *vmsave;
|
|
|
69 |
ulong sid;
|
|
|
70 |
int code;
|
|
|
71 |
gs_state *prev;
|
|
|
72 |
|
|
|
73 |
if (I_VALIDATE_BEFORE_SAVE)
|
|
|
74 |
ivalidate_clean_spaces(i_ctx_p);
|
|
|
75 |
ialloc_set_space(idmemory, avm_local);
|
|
|
76 |
vmsave = ialloc_struct(vm_save_t, &st_vm_save, "zsave");
|
|
|
77 |
ialloc_set_space(idmemory, space);
|
|
|
78 |
if (vmsave == 0)
|
|
|
79 |
return_error(e_VMerror);
|
|
|
80 |
sid = alloc_save_state(idmemory, vmsave);
|
|
|
81 |
if (sid == 0) {
|
|
|
82 |
ifree_object(vmsave, "zsave");
|
|
|
83 |
return_error(e_VMerror);
|
|
|
84 |
}
|
|
|
85 |
if_debug2('u', "[u]vmsave 0x%lx, id = %lu\n",
|
|
|
86 |
(ulong) vmsave, (ulong) sid);
|
|
|
87 |
code = gs_gsave_for_save(igs, &prev);
|
|
|
88 |
if (code < 0)
|
|
|
89 |
return code;
|
|
|
90 |
code = gs_gsave(igs);
|
|
|
91 |
if (code < 0)
|
|
|
92 |
return code;
|
|
|
93 |
vmsave->gsave = prev;
|
|
|
94 |
push(1);
|
|
|
95 |
make_tav(op, t_save, 0, saveid, sid);
|
|
|
96 |
if (I_VALIDATE_AFTER_SAVE)
|
|
|
97 |
ivalidate_clean_spaces(i_ctx_p);
|
|
|
98 |
return 0;
|
|
|
99 |
}
|
|
|
100 |
|
|
|
101 |
/* <save> restore - */
|
|
|
102 |
private int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *);
|
|
|
103 |
private int restore_check_stack(const ref_stack_t *, const alloc_save_t *, bool);
|
|
|
104 |
private void restore_fix_stack(ref_stack_t *, const alloc_save_t *, bool);
|
|
|
105 |
int
|
|
|
106 |
zrestore(i_ctx_t *i_ctx_p)
|
|
|
107 |
{
|
|
|
108 |
os_ptr op = osp;
|
|
|
109 |
alloc_save_t *asave;
|
|
|
110 |
bool last;
|
|
|
111 |
vm_save_t *vmsave;
|
|
|
112 |
int code = restore_check_operand(op, &asave, idmemory);
|
|
|
113 |
|
|
|
114 |
if (code < 0)
|
|
|
115 |
return code;
|
|
|
116 |
if_debug2('u', "[u]vmrestore 0x%lx, id = %lu\n",
|
|
|
117 |
(ulong) alloc_save_client_data(asave),
|
|
|
118 |
(ulong) op->value.saveid);
|
|
|
119 |
if (I_VALIDATE_BEFORE_RESTORE)
|
|
|
120 |
ivalidate_clean_spaces(i_ctx_p);
|
|
|
121 |
/* Check the contents of the stacks. */
|
|
|
122 |
osp--;
|
|
|
123 |
{
|
|
|
124 |
int code;
|
|
|
125 |
|
|
|
126 |
if ((code = restore_check_stack(&o_stack, asave, false)) < 0 ||
|
|
|
127 |
(code = restore_check_stack(&e_stack, asave, true)) < 0 ||
|
|
|
128 |
(code = restore_check_stack(&d_stack, asave, false)) < 0
|
|
|
129 |
) {
|
|
|
130 |
osp++;
|
|
|
131 |
return code;
|
|
|
132 |
}
|
|
|
133 |
}
|
|
|
134 |
/* Reset l_new in all stack entries if the new save level is zero. */
|
|
|
135 |
/* Also do some special fixing on the e-stack. */
|
|
|
136 |
restore_fix_stack(&o_stack, asave, false);
|
|
|
137 |
restore_fix_stack(&e_stack, asave, true);
|
|
|
138 |
restore_fix_stack(&d_stack, asave, false);
|
|
|
139 |
/* Iteratively restore the state of memory, */
|
|
|
140 |
/* also doing a grestoreall at each step. */
|
|
|
141 |
do {
|
|
|
142 |
vmsave = alloc_save_client_data(alloc_save_current(idmemory));
|
|
|
143 |
/* Restore the graphics state. */
|
|
|
144 |
gs_grestoreall_for_restore(igs, vmsave->gsave);
|
|
|
145 |
/*
|
|
|
146 |
* If alloc_save_space decided to do a second save, the vmsave
|
|
|
147 |
* object was allocated one save level less deep than the
|
|
|
148 |
* current level, so ifree_object won't actually free it;
|
|
|
149 |
* however, it points to a gsave object that definitely
|
|
|
150 |
* *has* been freed. In order not to trip up the garbage
|
|
|
151 |
* collector, we clear the gsave pointer now.
|
|
|
152 |
*/
|
|
|
153 |
vmsave->gsave = 0;
|
|
|
154 |
/* Now it's safe to restore the state of memory. */
|
|
|
155 |
last = alloc_restore_state_step(asave);
|
|
|
156 |
}
|
|
|
157 |
while (!last);
|
|
|
158 |
{
|
|
|
159 |
uint space = icurrent_space;
|
|
|
160 |
|
|
|
161 |
ialloc_set_space(idmemory, avm_local);
|
|
|
162 |
ifree_object(vmsave, "zrestore");
|
|
|
163 |
ialloc_set_space(idmemory, space);
|
|
|
164 |
}
|
|
|
165 |
dict_set_top(); /* reload dict stack cache */
|
|
|
166 |
if (I_VALIDATE_AFTER_RESTORE)
|
|
|
167 |
ivalidate_clean_spaces(i_ctx_p);
|
|
|
168 |
/* If the i_ctx_p LockFilePermissions is true, but the userparams */
|
|
|
169 |
/* we just restored is false, we need to make sure that we do not */
|
|
|
170 |
/* cause an 'invalidaccess' in setuserparams. Temporarily set */
|
|
|
171 |
/* LockFilePermissions false until the gs_lev2.ps can do a */
|
|
|
172 |
/* setuserparams from the restored userparam dictionary. */
|
|
|
173 |
i_ctx_p->LockFilePermissions = false;
|
|
|
174 |
return 0;
|
|
|
175 |
}
|
|
|
176 |
/* Check the operand of a restore. */
|
|
|
177 |
private int
|
|
|
178 |
restore_check_operand(os_ptr op, alloc_save_t ** pasave,
|
|
|
179 |
gs_dual_memory_t *idmem)
|
|
|
180 |
{
|
|
|
181 |
vm_save_t *vmsave;
|
|
|
182 |
ulong sid;
|
|
|
183 |
alloc_save_t *asave;
|
|
|
184 |
|
|
|
185 |
check_type(*op, t_save);
|
|
|
186 |
vmsave = r_ptr(op, vm_save_t);
|
|
|
187 |
if (vmsave == 0) /* invalidated save */
|
|
|
188 |
return_error(e_invalidrestore);
|
|
|
189 |
sid = op->value.saveid;
|
|
|
190 |
asave = alloc_find_save(idmem, sid);
|
|
|
191 |
if (asave == 0)
|
|
|
192 |
return_error(e_invalidrestore);
|
|
|
193 |
*pasave = asave;
|
|
|
194 |
return 0;
|
|
|
195 |
}
|
|
|
196 |
/* Check a stack to make sure all its elements are older than a save. */
|
|
|
197 |
private int
|
|
|
198 |
restore_check_stack(const ref_stack_t * pstack, const alloc_save_t * asave,
|
|
|
199 |
bool is_estack)
|
|
|
200 |
{
|
|
|
201 |
ref_stack_enum_t rsenum;
|
|
|
202 |
|
|
|
203 |
ref_stack_enum_begin(&rsenum, pstack);
|
|
|
204 |
do {
|
|
|
205 |
const ref *stkp = rsenum.ptr;
|
|
|
206 |
uint size = rsenum.size;
|
|
|
207 |
|
|
|
208 |
for (; size; stkp++, size--) {
|
|
|
209 |
const void *ptr;
|
|
|
210 |
|
|
|
211 |
switch (r_type(stkp)) {
|
|
|
212 |
case t_array:
|
|
|
213 |
ptr = stkp->value.refs;
|
|
|
214 |
break;
|
|
|
215 |
case t_dictionary:
|
|
|
216 |
ptr = stkp->value.pdict;
|
|
|
217 |
break;
|
|
|
218 |
case t_file:
|
|
|
219 |
/* Don't check executable or closed literal */
|
|
|
220 |
/* files on the e-stack. */
|
|
|
221 |
{
|
|
|
222 |
stream *s;
|
|
|
223 |
|
|
|
224 |
if (is_estack &&
|
|
|
225 |
(r_has_attr(stkp, a_executable) ||
|
|
|
226 |
file_is_invalid(s, stkp))
|
|
|
227 |
)
|
|
|
228 |
continue;
|
|
|
229 |
}
|
|
|
230 |
ptr = stkp->value.pfile;
|
|
|
231 |
break;
|
|
|
232 |
case t_name:
|
|
|
233 |
/* Names are special because of how they are allocated. */
|
|
|
234 |
if (alloc_name_is_since_save((const gs_memory_t *)pstack->memory,
|
|
|
235 |
stkp, asave))
|
|
|
236 |
return_error(e_invalidrestore);
|
|
|
237 |
continue;
|
|
|
238 |
case t_string:
|
|
|
239 |
/* Don't check empty executable strings */
|
|
|
240 |
/* on the e-stack. */
|
|
|
241 |
if (r_size(stkp) == 0 &&
|
|
|
242 |
r_has_attr(stkp, a_executable) && is_estack
|
|
|
243 |
)
|
|
|
244 |
continue;
|
|
|
245 |
ptr = stkp->value.bytes;
|
|
|
246 |
break;
|
|
|
247 |
case t_mixedarray:
|
|
|
248 |
case t_shortarray:
|
|
|
249 |
ptr = stkp->value.packed;
|
|
|
250 |
break;
|
|
|
251 |
case t_device:
|
|
|
252 |
ptr = stkp->value.pdevice;
|
|
|
253 |
break;
|
|
|
254 |
case t_fontID:
|
|
|
255 |
case t_struct:
|
|
|
256 |
case t_astruct:
|
|
|
257 |
ptr = stkp->value.pstruct;
|
|
|
258 |
break;
|
|
|
259 |
default:
|
|
|
260 |
continue;
|
|
|
261 |
}
|
|
|
262 |
if (alloc_is_since_save(ptr, asave))
|
|
|
263 |
return_error(e_invalidrestore);
|
|
|
264 |
}
|
|
|
265 |
} while (ref_stack_enum_next(&rsenum));
|
|
|
266 |
return 0; /* OK */
|
|
|
267 |
}
|
|
|
268 |
/*
|
|
|
269 |
* If the new save level is zero, fix up the contents of a stack
|
|
|
270 |
* by clearing the l_new bit in all the entries (since we can't tolerate
|
|
|
271 |
* values with l_new set if the save level is zero).
|
|
|
272 |
* Also, in any case, fix up the e-stack by replacing empty executable
|
|
|
273 |
* strings and closed executable files that are newer than the save
|
|
|
274 |
* with canonical ones that aren't.
|
|
|
275 |
*
|
|
|
276 |
* Note that this procedure is only called if restore_check_stack succeeded.
|
|
|
277 |
*/
|
|
|
278 |
private void
|
|
|
279 |
restore_fix_stack(ref_stack_t * pstack, const alloc_save_t * asave,
|
|
|
280 |
bool is_estack)
|
|
|
281 |
{
|
|
|
282 |
ref_stack_enum_t rsenum;
|
|
|
283 |
|
|
|
284 |
ref_stack_enum_begin(&rsenum, pstack);
|
|
|
285 |
do {
|
|
|
286 |
ref *stkp = rsenum.ptr;
|
|
|
287 |
uint size = rsenum.size;
|
|
|
288 |
|
|
|
289 |
for (; size; stkp++, size--) {
|
|
|
290 |
r_clear_attrs(stkp, l_new); /* always do it, no harm */
|
|
|
291 |
if (is_estack) {
|
|
|
292 |
ref ofile;
|
|
|
293 |
|
|
|
294 |
ref_assign(&ofile, stkp);
|
|
|
295 |
switch (r_type(stkp)) {
|
|
|
296 |
case t_string:
|
|
|
297 |
if (r_size(stkp) == 0 &&
|
|
|
298 |
alloc_is_since_save(stkp->value.bytes,
|
|
|
299 |
asave)
|
|
|
300 |
) {
|
|
|
301 |
make_empty_const_string(stkp,
|
|
|
302 |
avm_foreign);
|
|
|
303 |
break;
|
|
|
304 |
}
|
|
|
305 |
continue;
|
|
|
306 |
case t_file:
|
|
|
307 |
if (alloc_is_since_save(stkp->value.pfile,
|
|
|
308 |
asave)
|
|
|
309 |
) {
|
|
|
310 |
make_invalid_file(stkp);
|
|
|
311 |
break;
|
|
|
312 |
}
|
|
|
313 |
continue;
|
|
|
314 |
default:
|
|
|
315 |
continue;
|
|
|
316 |
}
|
|
|
317 |
r_copy_attrs(stkp, a_all | a_executable,
|
|
|
318 |
&ofile);
|
|
|
319 |
}
|
|
|
320 |
}
|
|
|
321 |
} while (ref_stack_enum_next(&rsenum));
|
|
|
322 |
}
|
|
|
323 |
|
|
|
324 |
/* - vmstatus <save_level> <vm_used> <vm_maximum> */
|
|
|
325 |
private int
|
|
|
326 |
zvmstatus(i_ctx_t *i_ctx_p)
|
|
|
327 |
{
|
|
|
328 |
os_ptr op = osp;
|
|
|
329 |
gs_memory_status_t mstat, dstat;
|
|
|
330 |
|
|
|
331 |
gs_memory_status(imemory, &mstat);
|
|
|
332 |
if (imemory == imemory_global) {
|
|
|
333 |
gs_memory_status_t sstat;
|
|
|
334 |
|
|
|
335 |
gs_memory_status(imemory_system, &sstat);
|
|
|
336 |
mstat.allocated += sstat.allocated;
|
|
|
337 |
mstat.used += sstat.used;
|
|
|
338 |
}
|
|
|
339 |
gs_memory_status(imemory->non_gc_memory, &dstat);
|
|
|
340 |
push(3);
|
|
|
341 |
make_int(op - 2, imemory_save_level(iimemory_local));
|
|
|
342 |
make_int(op - 1, mstat.used);
|
|
|
343 |
make_int(op, mstat.allocated + dstat.allocated - dstat.used);
|
|
|
344 |
return 0;
|
|
|
345 |
}
|
|
|
346 |
|
|
|
347 |
/* ------ Non-standard extensions ------ */
|
|
|
348 |
|
|
|
349 |
/* <save> .forgetsave - */
|
|
|
350 |
private int
|
|
|
351 |
zforgetsave(i_ctx_t *i_ctx_p)
|
|
|
352 |
{
|
|
|
353 |
os_ptr op = osp;
|
|
|
354 |
alloc_save_t *asave;
|
|
|
355 |
vm_save_t *vmsave;
|
|
|
356 |
int code = restore_check_operand(op, &asave, idmemory);
|
|
|
357 |
|
|
|
358 |
if (code < 0)
|
|
|
359 |
return 0;
|
|
|
360 |
vmsave = alloc_save_client_data(asave);
|
|
|
361 |
/* Reset l_new in all stack entries if the new save level is zero. */
|
|
|
362 |
restore_fix_stack(&o_stack, asave, false);
|
|
|
363 |
restore_fix_stack(&e_stack, asave, false);
|
|
|
364 |
restore_fix_stack(&d_stack, asave, false);
|
|
|
365 |
/*
|
|
|
366 |
* Forget the gsaves, by deleting the bottom gstate on
|
|
|
367 |
* the current stack and the top one on the saved stack and then
|
|
|
368 |
* concatenating the stacks together.
|
|
|
369 |
*/
|
|
|
370 |
{
|
|
|
371 |
gs_state *pgs = igs;
|
|
|
372 |
gs_state *last;
|
|
|
373 |
|
|
|
374 |
while (gs_state_saved(last = gs_state_saved(pgs)) != 0)
|
|
|
375 |
pgs = last;
|
|
|
376 |
gs_state_swap_saved(last, vmsave->gsave);
|
|
|
377 |
gs_grestore(last);
|
|
|
378 |
gs_grestore(last);
|
|
|
379 |
}
|
|
|
380 |
/* Forget the save in the memory manager. */
|
|
|
381 |
alloc_forget_save(asave);
|
|
|
382 |
{
|
|
|
383 |
uint space = icurrent_space;
|
|
|
384 |
|
|
|
385 |
ialloc_set_space(idmemory, avm_local);
|
|
|
386 |
/* See above for why we clear the gsave pointer here. */
|
|
|
387 |
vmsave->gsave = 0;
|
|
|
388 |
ifree_object(vmsave, "zrestore");
|
|
|
389 |
ialloc_set_space(idmemory, space);
|
|
|
390 |
}
|
|
|
391 |
pop(1);
|
|
|
392 |
return 0;
|
|
|
393 |
}
|
|
|
394 |
|
|
|
395 |
/* ------ Initialization procedure ------ */
|
|
|
396 |
|
|
|
397 |
const op_def zvmem_op_defs[] =
|
|
|
398 |
{
|
|
|
399 |
{"1.forgetsave", zforgetsave},
|
|
|
400 |
{"1restore", zrestore},
|
|
|
401 |
{"0save", zsave},
|
|
|
402 |
{"0vmstatus", zvmstatus},
|
|
|
403 |
op_def_end(0)
|
|
|
404 |
};
|