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