Coverage Report

Created: 2025-06-24 07:01

/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
}