/src/ghostpdl/psi/icontext.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Context state operations */ |
18 | | #include "ghost.h" |
19 | | #include "gsstruct.h" /* for gxalloc.h */ |
20 | | #include "gxalloc.h" |
21 | | #include "ierrors.h" |
22 | | #include "stream.h" /* for files.h */ |
23 | | #include "files.h" |
24 | | #include "idict.h" |
25 | | #include "igstate.h" |
26 | | #include "icontext.h" |
27 | | #include "interp.h" |
28 | | #include "isave.h" |
29 | | #include "dstack.h" |
30 | | #include "estack.h" |
31 | | #include "store.h" |
32 | | |
33 | | /* Declare the GC descriptors for embedded objects. */ |
34 | | extern_st(st_gs_dual_memory); |
35 | | extern_st(st_ref_stack); |
36 | | extern_st(st_dict_stack); |
37 | | extern_st(st_exec_stack); |
38 | | extern_st(st_op_stack); |
39 | | |
40 | | /* GC descriptors */ |
41 | | static |
42 | | CLEAR_MARKS_PROC(context_state_clear_marks) |
43 | 339k | { |
44 | 339k | gs_context_state_t *const pcst = vptr; |
45 | | |
46 | 339k | r_clear_attrs(&pcst->stdio[0], l_mark); |
47 | 339k | r_clear_attrs(&pcst->stdio[1], l_mark); |
48 | 339k | r_clear_attrs(&pcst->stdio[2], l_mark); |
49 | 339k | r_clear_attrs(&pcst->error_object, l_mark); |
50 | 339k | r_clear_attrs(&pcst->userparams, l_mark); |
51 | 339k | r_clear_attrs(&pcst->op_array_table_global.table, l_mark); |
52 | 339k | r_clear_attrs(&pcst->op_array_table_local.table, l_mark); |
53 | 339k | } |
54 | | static |
55 | 12.2M | ENUM_PTRS_WITH(context_state_enum_ptrs, gs_context_state_t *pcst) { |
56 | 4.75M | index -= 11; |
57 | 4.75M | if (index < st_gs_dual_memory_num_ptrs) |
58 | 0 | return ENUM_USING(st_gs_dual_memory, &pcst->memory, |
59 | 4.75M | sizeof(pcst->memory), index); |
60 | 4.75M | index -= st_gs_dual_memory_num_ptrs; |
61 | 4.75M | if (index < st_dict_stack_num_ptrs) |
62 | 1.35M | return ENUM_USING(st_dict_stack, &pcst->dict_stack, |
63 | 4.75M | sizeof(pcst->dict_stack), index); |
64 | 3.39M | index -= st_dict_stack_num_ptrs; |
65 | 3.39M | if (index < st_exec_stack_num_ptrs) |
66 | 1.35M | return ENUM_USING(st_exec_stack, &pcst->exec_stack, |
67 | 3.39M | sizeof(pcst->exec_stack), index); |
68 | 2.03M | index -= st_exec_stack_num_ptrs; |
69 | 2.03M | return ENUM_USING(st_op_stack, &pcst->op_stack, |
70 | 3.39M | sizeof(pcst->op_stack), index); |
71 | 3.39M | } |
72 | 3.39M | ENUM_PTR(0, gs_context_state_t, pgs); |
73 | 679k | case 1: ENUM_RETURN_REF(&pcst->stdio[0]); |
74 | 679k | case 2: ENUM_RETURN_REF(&pcst->stdio[1]); |
75 | 679k | case 3: ENUM_RETURN_REF(&pcst->stdio[2]); |
76 | 679k | case 4: ENUM_RETURN_REF(&pcst->error_object); |
77 | 3.39M | ENUM_PTR(5, gs_context_state_t, invalid_file_stream); |
78 | 679k | case 6: ENUM_RETURN_REF(&pcst->userparams); |
79 | 3.39M | ENUM_PTR(7, gs_context_state_t, op_array_table_global.nx_table); |
80 | 3.39M | ENUM_PTR(8, gs_context_state_t, op_array_table_local.nx_table); |
81 | 679k | case 9: ENUM_RETURN_REF(&pcst->op_array_table_global.table); |
82 | 679k | case 10: ENUM_RETURN_REF(&pcst->op_array_table_local.table); |
83 | 12.2M | ENUM_PTRS_END |
84 | 339k | static RELOC_PTRS_WITH(context_state_reloc_ptrs, gs_context_state_t *pcst); |
85 | 339k | RELOC_PTR(gs_context_state_t, pgs); |
86 | 339k | RELOC_USING(st_gs_dual_memory, &pcst->memory, sizeof(pcst->memory)); |
87 | | /******* WHY DON'T WE CLEAR THE l_mark OF stdio? ******/ |
88 | 339k | RELOC_REF_VAR(pcst->stdio[0]); |
89 | 339k | RELOC_REF_VAR(pcst->stdio[1]); |
90 | 339k | RELOC_REF_VAR(pcst->stdio[2]); |
91 | 339k | RELOC_PTR(gs_context_state_t, invalid_file_stream); |
92 | 339k | RELOC_REF_VAR(pcst->error_object); |
93 | 339k | r_clear_attrs(&pcst->error_object, l_mark); |
94 | 339k | RELOC_REF_VAR(pcst->userparams); |
95 | 339k | r_clear_attrs(&pcst->userparams, l_mark); |
96 | 339k | RELOC_PTR(gs_context_state_t, op_array_table_global.nx_table); |
97 | 339k | RELOC_PTR(gs_context_state_t, op_array_table_local.nx_table); |
98 | 339k | RELOC_REF_VAR(pcst->op_array_table_global.table); |
99 | 339k | r_clear_attrs(&pcst->op_array_table_global.table, l_mark); |
100 | 339k | RELOC_REF_VAR(pcst->op_array_table_local.table); |
101 | 339k | r_clear_attrs(&pcst->op_array_table_local.table, l_mark); |
102 | 339k | RELOC_USING(st_dict_stack, &pcst->dict_stack, sizeof(pcst->dict_stack)); |
103 | 339k | RELOC_USING(st_exec_stack, &pcst->exec_stack, sizeof(pcst->exec_stack)); |
104 | 339k | RELOC_USING(st_op_stack, &pcst->op_stack, sizeof(pcst->op_stack)); |
105 | 339k | RELOC_PTRS_END |
106 | | public_st_context_state(); |
107 | | |
108 | | /* Allocate the state of a context. */ |
109 | | int |
110 | | context_state_alloc(gs_context_state_t ** ppcst, |
111 | | const ref *psystem_dict, |
112 | | const gs_dual_memory_t * dmem) |
113 | 162k | { |
114 | 162k | gs_ref_memory_t *mem = dmem->space_local; |
115 | 162k | gs_context_state_t *pcst = *ppcst; |
116 | 162k | int code; |
117 | 162k | int i; |
118 | | |
119 | 162k | if (pcst == 0) { |
120 | 162k | pcst = gs_alloc_struct((gs_memory_t *) mem, gs_context_state_t, |
121 | 162k | &st_context_state, "context_state_alloc"); |
122 | 162k | if (pcst == 0) |
123 | 0 | return_error(gs_error_VMerror); |
124 | 162k | } |
125 | | /* Make sure pcst->memory is valid, in case we return an error. */ |
126 | 162k | pcst->memory = *dmem; |
127 | 162k | code = gs_interp_alloc_stacks(mem, pcst); |
128 | 162k | if (code < 0) |
129 | 0 | goto x0; |
130 | | /* |
131 | | * We have to initialize the dictionary stack early, |
132 | | * for far-off references to systemdict. |
133 | | */ |
134 | 162k | pcst->dict_stack.system_dict = *psystem_dict; |
135 | 162k | pcst->dict_stack.min_size = 0; |
136 | 162k | pcst->dict_stack.userdict_index = 0; |
137 | 162k | pcst->pgs = int_gstate_alloc(dmem); |
138 | 162k | if (pcst->pgs == 0) { |
139 | 0 | code = gs_note_error(gs_error_VMerror); |
140 | 0 | goto x1; |
141 | 0 | } |
142 | 162k | pcst->language_level = 1; |
143 | 162k | make_false(&pcst->array_packing); |
144 | 162k | make_int(&pcst->binary_object_format, 0); |
145 | 162k | pcst->nv_page_count = 0; |
146 | 162k | pcst->rand_state = rand_state_initial; |
147 | 162k | pcst->usertime_inited = false; |
148 | 162k | pcst->plugin_list = 0; |
149 | 162k | make_t(&pcst->error_object, t__invalid); |
150 | 162k | { /* |
151 | | * Create an empty userparams dictionary of the right size. |
152 | | * If we can't determine the size, pick an arbitrary one. |
153 | | */ |
154 | 162k | ref *puserparams; |
155 | 162k | uint size; |
156 | 162k | ref *system_dict = &pcst->dict_stack.system_dict; |
157 | | |
158 | 162k | if (dict_find_string(system_dict, "userparams", &puserparams) > 0) |
159 | 0 | size = dict_length(puserparams); |
160 | 162k | else |
161 | 162k | size = 300; |
162 | 162k | code = dict_alloc(pcst->memory.space_local, size, &pcst->userparams); |
163 | 162k | if (code < 0) |
164 | 0 | goto x2; |
165 | | /* PostScript code initializes the user parameters. */ |
166 | 162k | } |
167 | 162k | pcst->scanner_options = 0; |
168 | 162k | pcst->LockFilePermissions = false; |
169 | 162k | pcst->starting_arg_file = false; |
170 | 162k | pcst->RenderTTNotdef = true; |
171 | | |
172 | 162k | pcst->invalid_file_stream = (stream*)gs_alloc_struct_immovable(mem->stable_memory, |
173 | 162k | stream, &st_stream, |
174 | 162k | "context_state_alloc"); |
175 | 162k | if (pcst->invalid_file_stream == NULL) { |
176 | 0 | code = gs_note_error(gs_error_VMerror); |
177 | 0 | goto x3; |
178 | 0 | } |
179 | | |
180 | 162k | s_init(pcst->invalid_file_stream, mem->stable_memory); |
181 | 162k | sread_string(pcst->invalid_file_stream, NULL, 0); |
182 | 162k | s_init_no_id(pcst->invalid_file_stream); |
183 | | |
184 | | /* The initial stdio values are bogus.... */ |
185 | 162k | make_file(&pcst->stdio[0], a_readonly | avm_invalid_file_entry, 1, |
186 | 162k | pcst->invalid_file_stream); |
187 | 162k | make_file(&pcst->stdio[1], a_all | avm_invalid_file_entry, 1, |
188 | 162k | pcst->invalid_file_stream); |
189 | 162k | make_file(&pcst->stdio[2], a_all | avm_invalid_file_entry, 1, |
190 | 162k | pcst->invalid_file_stream); |
191 | 812k | for (i = countof(pcst->memory.spaces_indexed); --i >= 0;) |
192 | 649k | if (dmem->spaces_indexed[i] != 0) |
193 | 487k | ++(dmem->spaces_indexed[i]->num_contexts); |
194 | | /* The number of interpreter "ticks" between calls on the time_slice_proc. |
195 | | * Currently, the clock ticks before each operator, and at each |
196 | | * procedure return. */ |
197 | 162k | pcst->time_slice_ticks = 0x7fff; |
198 | 162k | *ppcst = pcst; |
199 | 162k | return 0; |
200 | 0 | x3:/* No need to delete dictionary here, as gc will do it for us. */ |
201 | 0 | x2:gs_gstate_free(pcst->pgs); |
202 | 0 | x1:gs_interp_free_stacks(mem, pcst); |
203 | 0 | x0:if (*ppcst == 0) |
204 | 0 | gs_free_object((gs_memory_t *) mem, pcst, "context_state_alloc"); |
205 | 0 | return code; |
206 | 0 | } |
207 | | |
208 | | /* Load the interpreter state from a context. */ |
209 | | int |
210 | | context_state_load(gs_context_state_t * i_ctx_p) |
211 | 502k | { |
212 | 502k | gs_ref_memory_t *lmem = iimemory_local; |
213 | 502k | ref *system_dict = systemdict; |
214 | 502k | uint space = r_space(system_dict); |
215 | 502k | dict_stack_t *dstack = &idict_stack; |
216 | 502k | int code; |
217 | | |
218 | | /* |
219 | | * Disable save checking, and space check for systemdict, while |
220 | | * copying dictionaries. |
221 | | */ |
222 | 502k | alloc_set_not_in_save(idmemory); |
223 | 502k | r_set_space(system_dict, avm_max); |
224 | | /* |
225 | | * Switch references from systemdict to local objects. |
226 | | * userdict.localdicts holds these objects. We could optimize this by |
227 | | * only doing it if we're changing to a different local VM relative to |
228 | | * the same global VM, but the cost is low enough relative to other |
229 | | * things that we don't bother. |
230 | | */ |
231 | 502k | { |
232 | 502k | ref_stack_t *rdstack = &dstack->stack; |
233 | 502k | const ref *puserdict = |
234 | 502k | ref_stack_index(rdstack, ref_stack_count(rdstack) - 1 - |
235 | 502k | dstack->userdict_index); |
236 | 502k | ref *plocaldicts; |
237 | | |
238 | 502k | if (dict_find_string(puserdict, "localdicts", &plocaldicts) > 0 && |
239 | 502k | r_has_type(plocaldicts, t_dictionary) |
240 | 502k | ) { |
241 | 0 | dict_copy(plocaldicts, system_dict, dstack); |
242 | 0 | } |
243 | 502k | } |
244 | | /* |
245 | | * Set systemdict.userparams to the saved copy, and then |
246 | | * set the actual user parameters. Note that we must disable both |
247 | | * space checking and save checking while doing this. Also, |
248 | | * we must do this after copying localdicts (if required), because |
249 | | * userparams also appears in localdicts. |
250 | | */ |
251 | 502k | code = dict_put_string(system_dict, "userparams", &i_ctx_p->userparams, |
252 | 502k | dstack); |
253 | 502k | if (code >= 0) |
254 | 502k | code = set_user_params(i_ctx_p, &i_ctx_p->userparams); |
255 | 502k | r_set_space(system_dict, space); |
256 | 502k | if (lmem->save_level > 0) |
257 | 177k | alloc_set_in_save(idmemory); |
258 | 502k | estack_clear_cache(&iexec_stack); |
259 | 502k | dstack_set_top(&idict_stack); |
260 | 502k | return code; |
261 | 502k | } |
262 | | |
263 | | /* Store the interpreter state in a context. */ |
264 | | int |
265 | | context_state_store(gs_context_state_t * pcst) |
266 | 339k | { |
267 | 339k | ref_stack_cleanup(&pcst->dict_stack.stack); |
268 | 339k | ref_stack_cleanup(&pcst->exec_stack.stack); |
269 | 339k | ref_stack_cleanup(&pcst->op_stack.stack); |
270 | | /* |
271 | | * The user parameters in systemdict.userparams are kept |
272 | | * up to date by PostScript code, but we still need to save |
273 | | * systemdict.userparams to get the correct l_new flag. |
274 | | */ |
275 | 339k | { |
276 | 339k | ref *puserparams; |
277 | | /* We need i_ctx_p for access to the d_stack. */ |
278 | 339k | i_ctx_t *i_ctx_p = pcst; |
279 | | |
280 | 339k | if (dict_find_string(systemdict, "userparams", &puserparams) <= 0) |
281 | 0 | return_error(gs_error_Fatal); |
282 | 339k | pcst->userparams = *puserparams; |
283 | 339k | } |
284 | 0 | return 0; |
285 | 339k | } |
286 | | |
287 | | /* Free the contents of the state of a context, always to its local VM. */ |
288 | | /* Return a mask of which of its VMs, if any, we freed. */ |
289 | | int |
290 | | context_state_free(gs_context_state_t * pcst) |
291 | 0 | { |
292 | 0 | gs_ref_memory_t *mem = pcst->memory.space_local; |
293 | 0 | int freed = 0; |
294 | 0 | int i; |
295 | | |
296 | | /* |
297 | | * If this context is the last one referencing a particular VM |
298 | | * (local / global / system), free the entire VM space; |
299 | | * otherwise, just free the context-related structures. |
300 | | */ |
301 | 0 | for (i = countof(pcst->memory.spaces_indexed); --i >= 0;) { |
302 | 0 | if (pcst->memory.spaces_indexed[i] != 0 && |
303 | 0 | !--(pcst->memory.spaces_indexed[i]->num_contexts) |
304 | 0 | ) { |
305 | | /****** FREE THE ENTIRE SPACE ******/ |
306 | 0 | freed |= 1 << i; |
307 | 0 | } |
308 | 0 | } |
309 | | /* |
310 | | * If we freed any spaces at all, we must have freed the local |
311 | | * VM where the context structure and its substructures were |
312 | | * allocated. |
313 | | */ |
314 | 0 | if (freed) |
315 | 0 | return freed; |
316 | 0 | { |
317 | 0 | gs_gstate *pgs = pcst->pgs; |
318 | |
|
319 | 0 | gs_grestoreall(pgs); |
320 | | /* Patch the saved pointer so we can do the last grestore. */ |
321 | 0 | { |
322 | 0 | gs_gstate *saved = gs_gstate_saved(pgs); |
323 | |
|
324 | 0 | gs_gstate_swap_saved(saved, saved); |
325 | 0 | } |
326 | 0 | gs_grestore(pgs); |
327 | 0 | gs_gstate_swap_saved(pgs, (gs_gstate *) 0); |
328 | 0 | gs_gstate_free(pgs); |
329 | 0 | } |
330 | | /****** FREE USERPARAMS ******/ |
331 | 0 | gs_interp_free_stacks(mem, pcst); |
332 | 0 | return 0; |
333 | 0 | } |