1 /*
2  * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it either under the terms of the GNU Lesser General Public
6  * License version 2.1 as published by the Free Software Foundation
7  * (the "LGPL") or, at your option, under the terms of the Mozilla
8  * Public License Version 1.1 (the "MPL"). If you do not alter this
9  * notice, a recipient may use your version of this file under either
10  * the MPL or the LGPL.
11  *
12  * You should have received a copy of the LGPL along with this library
13  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
15  * You should have received a copy of the MPL along with this library
16  * in the file COPYING-MPL-1.1
17  *
18  * The contents of this file are subject to the Mozilla Public License
19  * Version 1.1 (the "License"); you may not use this file except in
20  * compliance with the License. You may obtain a copy of the License at
21  * http://www.mozilla.org/MPL/
22  *
23  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
24  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
25  * the specific language governing rights and limitations.
26  *
27  * The Original Code is the cairo graphics library.
28  *
29  * The Initial Developer of the Original Code is Chris Wilson.
30  *
31  * Contributor(s):
32  *	Chris Wilson <chris@chris-wilson.co.uk>
33  */
34 
35 #include "config.h"
36 
37 #include "cairo-script-private.h"
38 #include "cairo.h"
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <math.h>
44 #include <assert.h>
45 
46 #ifndef MAX
47 #define MAX(a,b) (((a)>=(b))?(a):(b))
48 #endif
49 
50 csi_status_t
_csi_error(csi_status_t status)51 _csi_error (csi_status_t status)
52 {
53     return status;
54 }
55 
56 /* XXX track global/local memory, cap etc, mark/sweep GC */
57 void *
_csi_alloc(csi_t * ctx,int size)58 _csi_alloc (csi_t *ctx, int size)
59 {
60     return malloc (size);
61 }
62 
63 void *
_csi_alloc0(csi_t * ctx,int size)64 _csi_alloc0 (csi_t *ctx, int size)
65 {
66     void *ptr;
67 
68     ptr = _csi_alloc (ctx, size);
69     if (_csi_likely (ptr != NULL))
70 	memset (ptr, 0, size);
71 
72     return ptr;
73 }
74 
75 void *
_csi_realloc(csi_t * ctx,void * ptr,int size)76 _csi_realloc (csi_t *ctx, void *ptr, int size)
77 {
78     return realloc (ptr, size);
79 }
80 
81 void
_csi_free(csi_t * ctx,void * ptr)82 _csi_free (csi_t *ctx, void *ptr)
83 {
84     if (_csi_unlikely (ptr == NULL))
85 	return;
86 
87     free (ptr);
88 }
89 
90 void *
_csi_perm_alloc(csi_t * ctx,int size)91 _csi_perm_alloc (csi_t *ctx, int size)
92 {
93     csi_chunk_t *chunk;
94     void *ptr;
95 
96     size = (size + sizeof (void *)-1) & -sizeof (void *);
97 
98     chunk = ctx->perm_chunk;
99     if (chunk == NULL || chunk->rem < size) {
100 	int chunk_size = (size + 8191) & -8192;
101 	chunk = _csi_alloc (ctx, sizeof (csi_chunk_t) + chunk_size);
102 	if (_csi_unlikely (chunk == NULL))
103 	    return NULL;
104 
105 	chunk->rem = chunk_size;
106 	chunk->ptr = (char *) (chunk + 1);
107 	chunk->next = ctx->perm_chunk;
108 	ctx->perm_chunk = chunk;
109     }
110 
111     ptr = chunk->ptr;
112     chunk->ptr += size;
113     chunk->rem -= size;
114 
115     return ptr;
116 }
117 
118 void *
_csi_slab_alloc(csi_t * ctx,int size)119 _csi_slab_alloc (csi_t *ctx, int size)
120 {
121 #if CSI_DEBUG_MALLOC
122     return malloc (size);
123 #else
124     int chunk_size;
125     csi_chunk_t *chunk;
126     void *ptr;
127 
128     chunk_size = 2 * sizeof (void *);
129     chunk_size = (size + chunk_size - 1) / chunk_size;
130 
131     if (ctx->slabs[chunk_size].free_list) {
132 	ptr = ctx->slabs[chunk_size].free_list;
133 	ctx->slabs[chunk_size].free_list = *(void **) ptr;
134 	return ptr;
135     }
136 
137     chunk = ctx->slabs[chunk_size].chunk;
138     if (chunk == NULL || ! chunk->rem) {
139 	int cnt = MAX (128, 8192 / (chunk_size * 2 * sizeof (void *)));
140 
141 	chunk = _csi_alloc (ctx,
142 			    sizeof (csi_chunk_t) +
143 			    cnt * chunk_size * 2 * sizeof (void *));
144 	if (_csi_unlikely (chunk == NULL))
145 	    return NULL;
146 
147 	chunk->rem = cnt;
148 	chunk->ptr = (char *) (chunk + 1);
149 	chunk->next = ctx->slabs[chunk_size].chunk;
150 	ctx->slabs[chunk_size].chunk = chunk;
151     }
152 
153     ptr = chunk->ptr;
154     chunk->ptr += chunk_size * 2 * sizeof (void *);
155     chunk->rem--;
156 
157     return ptr;
158 #endif
159 }
160 
161 void
_csi_slab_free(csi_t * ctx,void * ptr,int size)162 _csi_slab_free (csi_t *ctx, void *ptr, int size)
163 {
164     int chunk_size;
165     void **free_list;
166 
167     if (_csi_unlikely (ptr == NULL))
168 	return;
169 
170 #if CSI_DEBUG_MALLOC
171     free (ptr);
172 #else
173     chunk_size = 2 * sizeof (void *);
174     chunk_size = (size + chunk_size - 1) / chunk_size;
175 
176     free_list = ptr;
177     *free_list = ctx->slabs[chunk_size].free_list;
178     ctx->slabs[chunk_size].free_list = ptr;
179 #endif
180 }
181 
182 csi_status_t
_csi_stack_push(csi_t * ctx,csi_stack_t * stack,const csi_object_t * obj)183 _csi_stack_push (csi_t *ctx, csi_stack_t *stack,
184 		 const csi_object_t *obj)
185 {
186     if (_csi_unlikely (stack->len == stack->size))
187 	return _csi_stack_push_internal (ctx, stack, obj);
188 
189     stack->objects[stack->len++] = *obj;
190     return CSI_STATUS_SUCCESS;
191 }
192 
193 static void
_csi_perm_fini(csi_t * ctx)194 _csi_perm_fini (csi_t *ctx)
195 {
196     while (ctx->perm_chunk != NULL) {
197 	csi_chunk_t *chunk = ctx->perm_chunk;
198 	ctx->perm_chunk = chunk->next;
199 	_csi_free (ctx, chunk);
200     }
201 }
202 
203 static void
_csi_slab_fini(csi_t * ctx)204 _csi_slab_fini (csi_t *ctx)
205 {
206     unsigned int i;
207 
208     for (i = 0; i < sizeof (ctx->slabs) / sizeof (ctx->slabs[0]); i++) {
209 	while (ctx->slabs[i].chunk != NULL) {
210 	    csi_chunk_t *chunk = ctx->slabs[i].chunk;
211 	    ctx->slabs[i].chunk = chunk->next;
212 	    _csi_free (ctx, chunk);
213 	}
214     }
215 }
216 
217 static csi_status_t
_add_operator(csi_t * ctx,csi_dictionary_t * dict,const csi_operator_def_t * def)218 _add_operator (csi_t *ctx,
219 	       csi_dictionary_t *dict,
220 	       const csi_operator_def_t *def)
221 {
222     csi_object_t name;
223     csi_object_t operator;
224     csi_status_t status;
225 
226     status = csi_name_new_static (ctx, &name, def->name);
227     if (status)
228 	return status;
229 
230     csi_operator_new (&operator, def->op);
231 
232     return csi_dictionary_put (ctx, dict, name.datum.name, &operator);
233 }
234 
235 static csi_status_t
_add_integer_constant(csi_t * ctx,csi_dictionary_t * dict,const csi_integer_constant_def_t * def)236 _add_integer_constant (csi_t *ctx,
237 		       csi_dictionary_t *dict,
238 		       const csi_integer_constant_def_t *def)
239 {
240     csi_object_t name;
241     csi_object_t constant;
242     csi_status_t status;
243 
244     status = csi_name_new_static (ctx, &name, def->name);
245     if (status)
246 	return status;
247 
248     csi_integer_new (&constant, def->value);
249 
250     return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
251 }
252 
253 static csi_status_t
_add_real_constant(csi_t * ctx,csi_dictionary_t * dict,const csi_real_constant_def_t * def)254 _add_real_constant (csi_t *ctx,
255 		    csi_dictionary_t *dict,
256 		    const csi_real_constant_def_t *def)
257 {
258     csi_object_t name;
259     csi_object_t constant;
260     csi_status_t status;
261 
262     status = csi_name_new_static (ctx, &name, def->name);
263     if (status)
264 	return status;
265 
266     csi_real_new (&constant, def->value);
267 
268     return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
269 }
270 
271 static csi_status_t
_init_dictionaries(csi_t * ctx)272 _init_dictionaries (csi_t *ctx)
273 {
274     csi_status_t status;
275     csi_stack_t *stack;
276     csi_object_t obj;
277     csi_dictionary_t *dict, *opcodes;
278     const csi_operator_def_t *odef;
279     const csi_integer_constant_def_t *idef;
280     const csi_real_constant_def_t *rdef;
281     unsigned n;
282 
283     stack = &ctx->dstack;
284 
285     status = _csi_stack_init (ctx, stack, 4);
286     if (_csi_unlikely (status))
287 	return status;
288 
289     /* systemdict */
290     status = csi_dictionary_new (ctx, &obj);
291     if (_csi_unlikely (status))
292 	return status;
293 
294     status = _csi_stack_push (ctx, stack, &obj);
295     if (_csi_unlikely (status))
296 	return status;
297 
298     dict = obj.datum.dictionary;
299 
300     status = csi_dictionary_new (ctx, &obj);
301     if (_csi_unlikely (status))
302 	return status;
303 
304     opcodes = obj.datum.dictionary;
305 
306     n = 0;
307     csi_integer_new (&obj, n);
308     status = csi_dictionary_put (ctx, opcodes, 0, &obj);
309     if (_csi_unlikely (status))
310 	return status;
311     ctx->opcode[n++] = NULL;
312 
313     /* fill systemdict with operators */
314     for (odef = _csi_operators (); odef->name != NULL; odef++) {
315 	status = _add_operator (ctx, dict, odef);
316 	if (_csi_unlikely (status))
317 	    return status;
318 
319 	if (! csi_dictionary_has (opcodes, (csi_name_t) odef->op)) {
320 	    csi_integer_new (&obj, n);
321 	    status = csi_dictionary_put (ctx,
322 		                         opcodes, (csi_name_t) odef->op, &obj);
323 	    if (_csi_unlikely (status))
324 		return status;
325 
326 	    assert (n < sizeof (ctx->opcode) / sizeof (ctx->opcode[0]));
327 	    ctx->opcode[n++] = odef->op;
328 	}
329     }
330     csi_dictionary_free (ctx, opcodes);
331 
332     /* add constants */
333     for (idef = _csi_integer_constants (); idef->name != NULL; idef++) {
334 	status = _add_integer_constant (ctx, dict, idef);
335 	if (_csi_unlikely (status))
336 	    return status;
337     }
338     for (rdef = _csi_real_constants (); rdef->name != NULL; rdef++) {
339 	status = _add_real_constant (ctx, dict, rdef);
340 	if (_csi_unlikely (status))
341 	    return status;
342     }
343 
344     /* and seal */
345     //dict.type &= ~CSI_OBJECT_ATTR_WRITABLE;
346 
347 
348     /* globaldict */
349     status = csi_dictionary_new (ctx, &obj);
350     if (_csi_unlikely (status))
351 	return status;
352     status = _csi_stack_push (ctx, stack, &obj);
353     if (_csi_unlikely (status))
354 	return status;
355 
356     /* userdict */
357     status = csi_dictionary_new (ctx, &obj);
358     if (_csi_unlikely (status))
359 	return status;
360     status = _csi_stack_push (ctx, stack, &obj);
361     if (_csi_unlikely (status))
362 	return status;
363 
364     return CSI_STATUS_SUCCESS;
365 }
366 
367 /* intern string */
368 
369 typedef struct _cairo_intern_string {
370     csi_hash_entry_t hash_entry;
371     int len;
372     char *string;
373 } csi_intern_string_t;
374 
375 static unsigned long
_intern_string_hash(const char * str,int len)376 _intern_string_hash (const char *str, int len)
377 {
378     const signed char *p = (const signed char *) str;
379     if (len > 0) {
380 	unsigned int h = *p;
381 
382 	while (--len)
383 	    h = (h << 5) - h + *++p;
384 
385 	return h;
386     }
387     return 0;
388 }
389 
390 static cairo_bool_t
_intern_string_equal(const void * _a,const void * _b)391 _intern_string_equal (const void *_a, const void *_b)
392 {
393     const csi_intern_string_t *a = _a;
394     const csi_intern_string_t *b = _b;
395 
396     if (a->len != b->len)
397 	return FALSE;
398 
399     return memcmp (a->string, b->string, a->len) == 0;
400 }
401 
402 static void
_csi_init(csi_t * ctx)403 _csi_init (csi_t *ctx)
404 {
405     csi_status_t status;
406 
407     memset (ctx, 0, sizeof (*ctx));
408 
409     ctx->status = CSI_STATUS_SUCCESS;
410     ctx->ref_count = 1;
411     ctx->scanner.line_number = -1;
412 
413     status = _csi_hash_table_init (&ctx->strings, _intern_string_equal);
414     if (status)
415 	goto FAIL;
416 
417     status = _csi_stack_init (ctx, &ctx->ostack, 2048);
418     if (status)
419 	goto FAIL;
420     status = _init_dictionaries (ctx);
421     if (status)
422 	goto FAIL;
423 
424     status = _csi_scanner_init (ctx, &ctx->scanner);
425     if (status)
426 	goto FAIL;
427 
428     return;
429 
430 FAIL:
431     if (ctx->status == CSI_STATUS_SUCCESS)
432 	ctx->status = status;
433 }
434 
435 static void
_csi_finish(csi_t * ctx)436 _csi_finish (csi_t *ctx)
437 {
438     _csi_stack_fini (ctx, &ctx->ostack);
439     _csi_stack_fini (ctx, &ctx->dstack);
440     _csi_scanner_fini (ctx, &ctx->scanner);
441 
442     _csi_hash_table_fini (&ctx->strings);
443 }
444 
445 csi_status_t
_csi_name_define(csi_t * ctx,csi_name_t name,csi_object_t * obj)446 _csi_name_define (csi_t *ctx, csi_name_t name, csi_object_t *obj)
447 {
448     return csi_dictionary_put (ctx,
449 			ctx->dstack.objects[ctx->dstack.len-1].datum.dictionary,
450 			name,
451 			obj);
452 }
453 
454 csi_status_t
_csi_name_lookup(csi_t * ctx,csi_name_t name,csi_object_t * obj)455 _csi_name_lookup (csi_t *ctx, csi_name_t name, csi_object_t *obj)
456 {
457     int i;
458 
459     for (i = ctx->dstack.len; i--; ) {
460 	csi_dictionary_t *dict;
461 	csi_dictionary_entry_t *entry;
462 
463 	dict = ctx->dstack.objects[i].datum.dictionary;
464 	entry = _csi_hash_table_lookup (&dict->hash_table,
465 					(csi_hash_entry_t *) &name);
466 	if (entry != NULL) {
467 	    *obj = entry->value;
468 	    return CSI_STATUS_SUCCESS;
469 	}
470     }
471 
472     return _csi_error (CSI_STATUS_INVALID_SCRIPT);
473 }
474 
475 csi_status_t
_csi_name_undefine(csi_t * ctx,csi_name_t name)476 _csi_name_undefine (csi_t *ctx, csi_name_t name)
477 {
478     unsigned int i;
479 
480     for (i = ctx->dstack.len; --i; ) {
481 	if (csi_dictionary_has (ctx->dstack.objects[i].datum.dictionary,
482 				name))
483 	{
484 	    csi_dictionary_remove (ctx,
485 				   ctx->dstack.objects[i].datum.dictionary,
486 				   name);
487 	    return CSI_STATUS_SUCCESS;
488 	}
489     }
490 
491     return _csi_error (CSI_STATUS_INVALID_SCRIPT);
492 }
493 
494 csi_status_t
_csi_intern_string(csi_t * ctx,const char ** str_inout,int len)495 _csi_intern_string (csi_t *ctx, const char **str_inout, int len)
496 {
497     char *str = (char *) *str_inout;
498     csi_intern_string_t tmpl, *istring;
499     csi_status_t status = CSI_STATUS_SUCCESS;
500 
501     tmpl.hash_entry.hash = _intern_string_hash (str, len);
502     tmpl.len = len;
503     tmpl.string = (char *) str;
504 
505     istring = _csi_hash_table_lookup (&ctx->strings, &tmpl.hash_entry);
506     if (istring == NULL) {
507 	istring = _csi_perm_alloc (ctx,
508 				   sizeof (csi_intern_string_t) + len + 1);
509 	if (istring != NULL) {
510 	    istring->hash_entry.hash = tmpl.hash_entry.hash;
511 	    istring->len = tmpl.len;
512 	    istring->string = (char *) (istring + 1);
513 	    memcpy (istring->string, str, len);
514 	    istring->string[len] = '\0';
515 
516 	    status = _csi_hash_table_insert (&ctx->strings,
517 					     &istring->hash_entry);
518 	    if (_csi_unlikely (status)) {
519 		_csi_free (ctx, istring);
520 		return status;
521 	    }
522 	} else
523 	    return _csi_error (CSI_STATUS_NO_MEMORY);
524     }
525 
526     *str_inout = istring->string;
527     return CSI_STATUS_SUCCESS;
528 }
529 
530 /* Public */
531 
532 static csi_t _csi_nil = { -1, CSI_STATUS_NO_MEMORY };
533 
534 csi_t *
cairo_script_interpreter_create(void)535 cairo_script_interpreter_create (void)
536 {
537     csi_t *ctx;
538 
539     ctx = malloc (sizeof (csi_t));
540     if (ctx == NULL)
541 	return (csi_t *) &_csi_nil;
542 
543     _csi_init (ctx);
544 
545     return ctx;
546 }
547 
548 void
cairo_script_interpreter_install_hooks(csi_t * ctx,const csi_hooks_t * hooks)549 cairo_script_interpreter_install_hooks (csi_t *ctx,
550 					const csi_hooks_t *hooks)
551 {
552     if (ctx->status)
553 	return;
554 
555     ctx->hooks = *hooks;
556 }
557 
558 cairo_status_t
cairo_script_interpreter_run(csi_t * ctx,const char * filename)559 cairo_script_interpreter_run (csi_t *ctx, const char *filename)
560 {
561     csi_object_t file;
562 
563     if (ctx->status)
564 	return ctx->status;
565     if (ctx->finished)
566 	return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
567 
568     ctx->status = csi_file_new (ctx, &file, filename, "r");
569     if (ctx->status)
570 	return ctx->status;
571 
572     file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
573 
574     ctx->status = csi_object_execute (ctx, &file);
575     csi_object_free (ctx, &file);
576 
577     return ctx->status;
578 }
579 
580 cairo_status_t
cairo_script_interpreter_feed_stream(csi_t * ctx,FILE * stream)581 cairo_script_interpreter_feed_stream (csi_t *ctx, FILE *stream)
582 {
583     csi_object_t file;
584 
585     if (ctx->status)
586 	return ctx->status;
587     if (ctx->finished)
588 	return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
589 
590     ctx->status = csi_file_new_for_stream (ctx, &file, stream);
591     if (ctx->status)
592 	return ctx->status;
593 
594     file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
595 
596     ctx->status = csi_object_execute (ctx, &file);
597     csi_object_free (ctx, &file);
598 
599     return ctx->status;
600 }
601 
602 cairo_status_t
cairo_script_interpreter_feed_string(csi_t * ctx,const char * line,int len)603 cairo_script_interpreter_feed_string (csi_t *ctx, const char *line, int len)
604 {
605     csi_object_t file;
606 
607     if (ctx->status)
608 	return ctx->status;
609     if (ctx->finished)
610 	return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
611 
612     if (len < 0)
613 	len = strlen (line);
614     ctx->status = csi_file_new_for_bytes (ctx, &file, line, len);
615     if (ctx->status)
616 	return ctx->status;
617 
618     file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
619 
620     ctx->status = csi_object_execute (ctx, &file);
621     csi_object_free (ctx, &file);
622 
623     return ctx->status;
624 }
625 
626 unsigned int
cairo_script_interpreter_get_line_number(csi_t * ctx)627 cairo_script_interpreter_get_line_number (csi_t *ctx)
628 {
629     return ctx->scanner.line_number + 1; /* 1 index based */
630 }
631 
632 csi_t *
cairo_script_interpreter_reference(csi_t * ctx)633 cairo_script_interpreter_reference (csi_t *ctx)
634 {
635     ctx->ref_count++;
636     return ctx;
637 }
638 slim_hidden_def (cairo_script_interpreter_reference);
639 
640 cairo_status_t
cairo_script_interpreter_finish(csi_t * ctx)641 cairo_script_interpreter_finish (csi_t *ctx)
642 {
643     csi_status_t status;
644 
645     status = ctx->status;
646     if (! ctx->finished) {
647 	_csi_finish (ctx);
648 	ctx->finished = 1;
649     } else if (status == CSI_STATUS_SUCCESS) {
650 	status = ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
651     }
652 
653     return status;
654 }
655 
656 static void
_csi_fini(csi_t * ctx)657 _csi_fini (csi_t *ctx)
658 {
659     if (! ctx->finished)
660 	_csi_finish (ctx);
661 
662     if (ctx->free_array != NULL)
663 	csi_array_free (ctx, ctx->free_array);
664     if (ctx->free_dictionary != NULL)
665 	csi_dictionary_free (ctx, ctx->free_dictionary);
666     if (ctx->free_string != NULL)
667 	csi_string_free (ctx, ctx->free_string);
668 
669     _csi_slab_fini (ctx);
670     _csi_perm_fini (ctx);
671 }
672 
673 cairo_status_t
cairo_script_interpreter_destroy(csi_t * ctx)674 cairo_script_interpreter_destroy (csi_t *ctx)
675 {
676     csi_status_t status;
677 
678     status = ctx->status;
679     if (--ctx->ref_count)
680 	return status;
681 
682     _csi_fini (ctx);
683     free (ctx);
684 
685     return status;
686 }
687 slim_hidden_def (cairo_script_interpreter_destroy);
688 
689 cairo_status_t
cairo_script_interpreter_translate_stream(FILE * stream,cairo_write_func_t write_func,void * closure)690 cairo_script_interpreter_translate_stream (FILE *stream,
691 	                                   cairo_write_func_t write_func,
692 					   void *closure)
693 {
694     csi_t ctx;
695     csi_object_t src;
696     csi_status_t status;
697 
698     _csi_init (&ctx);
699 
700     status = csi_file_new_for_stream (&ctx, &src, stream);
701     if (status)
702 	goto BAIL;
703 
704     status = _csi_translate_file (&ctx, src.datum.file, write_func, closure);
705 
706 BAIL:
707     csi_object_free (&ctx, &src);
708     _csi_fini (&ctx);
709 
710     return status;
711 }
712