1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2014 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  *    if any, must include the following acknowledgment:
22  *       "This product includes software developed by the
23  *        Kannel Group (http://www.kannel.org/)."
24  *    Alternately, this acknowledgment may appear in the software itself,
25  *    if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  *    endorse or promote products derived from this software without
29  *    prior written permission. For written permission, please
30  *    contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  *    nor may "Kannel" appear in their name, without prior written
34  *    permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group.  For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  *
59  * ws.c
60  *
61  * Author: Markku Rossi <mtr@iki.fi>
62  *
63  * Copyright (c) 1999-2000 WAPIT OY LTD.
64  *		 All rights reserved.
65  *
66  * Public entry points to the WMLScript compiler and the main compile
67  * function.
68  *
69  */
70 
71 #include "wsint.h"
72 #include "ws.h"
73 #include "wsstree.h"
74 #include "wsasm.h"
75 
76 /********************* Types and definitions ****************************/
77 
78 #define WS_CHECK_COMPILE_ERROR()				\
79     do {							\
80         if (compiler->errors != 0) {                    	\
81             if (compiler->errors & WS_ERROR_B_MEMORY)		\
82                 result = WS_ERROR_OUT_OF_MEMORY;		\
83             else if (compiler->errors & WS_ERROR_B_SYNTAX)	\
84                 result = WS_ERROR_SYNTAX;			\
85             else if (compiler->errors & WS_ERROR_B_SEMANTIC)	\
86                 result = WS_ERROR_SEMANTIC;			\
87             else						\
88                 /* This shouldn't happen. */			\
89                 result = WS_ERROR;				\
90             goto out;						\
91         }							\
92     } while (0);
93 
94 /********************* Static variables *********************************/
95 
96 /* Human readable names for the result codes. */
97 static struct
98 {
99     WsResult code;
100     char *description;
101 } result_codes[] = {
102     { WS_OK, "success" },
103     { WS_ERROR_OUT_OF_MEMORY, "out of memory" },
104     { WS_ERROR_SYNTAX, "syntax error" },
105     { WS_ERROR_SEMANTIC, "compile error" },
106     { WS_ERROR_IO, "IO error" },
107     { WS_ERROR, "error" },
108 
109     { 0, NULL },
110 };
111 
112 /********************* Prototypes for static functions ******************/
113 
114 /* Compile the input stream `input' with the compiler `compiler' and
115    return the byte-code in `output_return' and its length in
116    `output_len_return'.  The function returns a WsResult error code
117    describing the success of the compilation. */
118 static WsResult compile_stream(WsCompilerPtr compiler,
119                                const char *input_name, WsStream *input,
120                                unsigned char **output_return,
121                                size_t *output_len_return);
122 
123 /* The default I/O function to send the output to stdout and stderr.
124    The argument `context' must be a `FILE *' file to which the output
125    is written. */
126 static void std_io(const char *data, size_t len, void *context);
127 
128 /* A comparison function for functions entries when sorting them by
129    their usage counts.  The function is stable maintaining their
130    original order if their usage counts are equal. */
131 static int sort_functions_cmp(const void *a, const void *b);
132 
133 /********************* Global functions *********************************/
134 
ws_create(WsCompilerParams * params)135 WsCompilerPtr ws_create(WsCompilerParams *params)
136 {
137     WsCompilerPtr compiler = ws_calloc(1, sizeof(*compiler));
138 
139     if (compiler == NULL)
140         return NULL;
141 
142     /* Store user params if specified. */
143     if (params)
144         compiler->params = *params;
145 
146     /* Basic initialization. */
147 
148     compiler->magic = COMPILER_MAGIC;
149 
150     if (compiler->params.stdout_cb == NULL) {
151         compiler->params.stdout_cb = std_io;
152         compiler->params.stdout_cb_context = stdout;
153     }
154     if (compiler->params.stderr_cb == NULL) {
155         compiler->params.stderr_cb = std_io;
156         compiler->params.stderr_cb_context = stderr;
157     }
158 
159     return compiler;
160 }
161 
162 
ws_destroy(WsCompilerPtr compiler)163 void ws_destroy(WsCompilerPtr compiler)
164 {
165     if (compiler == NULL)
166         return;
167 
168     ws_free(compiler);
169 
170 #if WS_MEM_DEBUG
171     if (ws_has_leaks())
172         ws_dump_blocks();
173 #endif /* WS_MEM_DEBUG */
174 }
175 
176 
ws_compile_file(WsCompilerPtr compiler,const char * input_name,FILE * input,FILE * output)177 WsResult ws_compile_file(WsCompilerPtr compiler, const char *input_name,
178                          FILE *input, FILE *output)
179 {
180     WsResult result;
181     WsStream *stream;
182     unsigned char *bc;
183     size_t bc_len;
184 
185     /* Initialize the input stream. */
186     stream = ws_stream_new_file(input, WS_FALSE, WS_FALSE);
187     if (stream == NULL)
188         return WS_ERROR_OUT_OF_MEMORY;
189 
190     result = compile_stream(compiler, input_name, stream, &bc, &bc_len);
191 
192     ws_stream_close(stream);
193 
194     if (result == WS_OK) {
195         /* Store the result to the file. */
196         if (fwrite(bc, 1, bc_len, output) != bc_len)
197             result = WS_ERROR_IO;
198 
199         ws_bc_data_free(bc);
200     }
201 
202     return result;
203 }
204 
205 
ws_compile_data(WsCompilerPtr compiler,const char * input_name,const unsigned char * input,size_t input_len,unsigned char ** output_return,size_t * output_len_return)206 WsResult ws_compile_data(WsCompilerPtr compiler, const char *input_name,
207                          const unsigned char *input, size_t input_len,
208                          unsigned char **output_return,
209                          size_t *output_len_return)
210 {
211     WsResult result;
212     WsStream *stream;
213 
214     /* Initialize the input stream. */
215     stream = ws_stream_new_data_input(input, input_len);
216     if (stream == NULL)
217         return WS_ERROR_OUT_OF_MEMORY;
218 
219     result = compile_stream(compiler, input_name, stream, output_return,
220                             output_len_return);
221 
222     ws_stream_close(stream);
223 
224     return result;
225 }
226 
227 
ws_free_byte_code(unsigned char * byte_code)228 void ws_free_byte_code(unsigned char *byte_code)
229 {
230     ws_bc_data_free(byte_code);
231 }
232 
233 
ws_result_to_string(WsResult result)234 const char *ws_result_to_string(WsResult result)
235 {
236     int i;
237 
238     for (i = 0; result_codes[i].description; i++) {
239         if (result_codes[i].code == result)
240             return result_codes[i].description;
241     }
242 
243     return "unknown result code";
244 }
245 
246 /********************* Lexer's memory handling helpers ******************/
247 
ws_lexer_register_block(WsCompiler * compiler,void * ptr)248 WsBool ws_lexer_register_block(WsCompiler *compiler, void *ptr)
249 {
250     void **n;
251 
252     if (ptr == NULL)
253         return WS_TRUE;
254 
255     n = ws_realloc(compiler->lexer_active_list,
256                    ((compiler->lexer_active_list_size + 1) * sizeof(void *)));
257     if (n == NULL)
258         return WS_FALSE;
259 
260     compiler->lexer_active_list = n;
261     compiler->lexer_active_list[compiler->lexer_active_list_size++] = ptr;
262 
263     return WS_TRUE;
264 }
265 
266 
ws_lexer_register_utf8(WsCompiler * compiler,WsUtf8String * string)267 WsBool ws_lexer_register_utf8(WsCompiler *compiler, WsUtf8String *string)
268 {
269     if (!ws_lexer_register_block(compiler, string))
270         return WS_FALSE;
271 
272     if (!ws_lexer_register_block(compiler, string->data)) {
273         compiler->lexer_active_list_size--;
274         return WS_FALSE;
275     }
276 
277     return WS_TRUE;
278 }
279 
280 
ws_lexer_free_block(WsCompiler * compiler,void * ptr)281 void ws_lexer_free_block(WsCompiler *compiler, void *ptr)
282 {
283     int i;
284 
285     if (ptr == NULL)
286         return;
287 
288     for (i = compiler->lexer_active_list_size - 1; i >= 0; i--) {
289         if (compiler->lexer_active_list[i] == ptr) {
290             memmove(&compiler->lexer_active_list[i],
291                     &compiler->lexer_active_list[i + 1],
292                     (compiler->lexer_active_list_size - i - 1) * sizeof(void *));
293             compiler->lexer_active_list_size--;
294 
295             ws_free(ptr);
296             return;
297         }
298     }
299 
300     ws_fatal("ws_lexer_free_block(): unknown block 0x%lx",
301              (unsigned long) ptr);
302 }
303 
304 
ws_lexer_free_utf8(WsCompiler * compiler,WsUtf8String * string)305 void ws_lexer_free_utf8(WsCompiler *compiler, WsUtf8String *string)
306 {
307     if (string == NULL)
308         return;
309 
310     ws_lexer_free_block(compiler, string->data);
311     ws_lexer_free_block(compiler, string);
312 }
313 
314 /********************* Static functions *********************************/
315 
compile_stream(WsCompilerPtr compiler,const char * input_name,WsStream * input,unsigned char ** output_return,size_t * output_len_return)316 static WsResult compile_stream(WsCompilerPtr compiler, const char *input_name,
317                                WsStream *input, unsigned char **output_return,
318                                size_t *output_len_return)
319 {
320     WsResult result = WS_OK;
321     WsUInt32 i;
322     WsListItem *li;
323     WsUInt8 findex;
324     WsUInt8 num_locals;
325     WsBcStringEncoding string_encoding = WS_BC_STRING_ENC_UTF8;
326 
327     /* Initialize the compiler context. */
328 
329     compiler->linenum = 1;
330     compiler->input_name = input_name;
331 
332     compiler->num_errors = 0;
333     compiler->num_warnings = 0;
334     compiler->num_extern_functions = 0;
335     compiler->num_local_functions = 0;
336     compiler->errors = 0;
337     compiler->last_syntax_error_line = 0;
338 
339     /* Allocate fast-malloc pool for the syntax tree. */
340 
341     compiler->pool_stree = ws_f_create(1024 * 1024);
342     if (compiler->pool_stree == NULL) {
343         result = WS_ERROR_OUT_OF_MEMORY;
344         goto out;
345     }
346 
347     /* Allocate hash tables. */
348 
349     compiler->pragma_use_hash = ws_pragma_use_hash_create();
350     if (compiler->pragma_use_hash == NULL) {
351         result = WS_ERROR_OUT_OF_MEMORY;
352         goto out;
353     }
354 
355     compiler->functions_hash = ws_function_hash_create();
356     if (compiler->functions_hash == NULL) {
357         result = WS_ERROR_OUT_OF_MEMORY;
358         goto out;
359     }
360 
361     /* Allocate a byte-code module. */
362 
363     if (compiler->params.use_latin1_strings)
364         string_encoding = WS_BC_STRING_ENC_ISO_8859_1;
365 
366     compiler->bc = ws_bc_alloc(string_encoding);
367     if (compiler->bc == NULL) {
368         result = WS_ERROR_OUT_OF_MEMORY;
369         goto out;
370     }
371 
372     /* Save the input stream. */
373     compiler->input = input;
374 
375     /* Parse the input. */
376 #if WS_DEBUG
377     global_compiler = compiler;
378 #endif /* WS_DEBUG */
379 
380     ws_yy_parse(compiler);
381 
382     /* Free all lexer's active not freed blocks.  If we have any blocks
383        on the used list, our compilation was not successful. */
384     {
385         size_t j;
386 
387         for (j = 0; j < compiler->lexer_active_list_size; j++)
388             ws_free(compiler->lexer_active_list[j]);
389         ws_free(compiler->lexer_active_list);
390 
391         compiler->lexer_active_list = NULL;
392     }
393 
394     WS_CHECK_COMPILE_ERROR();
395 
396     /* Sort functions if allowed and it helps. */
397     if (!compiler->params.no_opt_sort_bc_functions
398         && compiler->num_functions > 7) {
399         WsUInt32 i;
400 
401         ws_info(compiler, "optimize: sorting functions");
402 
403         /* Fetch the usage counts from the functions hash. */
404         for (i = 0; i < compiler->num_functions; i++) {
405             WsFunctionHash *fh = ws_function_hash(compiler,
406                                                   compiler->functions[i].name);
407             compiler->functions[i].usage_count = fh->usage_count;
408         }
409 
410         /* Sort functions.  */
411         qsort(compiler->functions, compiler->num_functions,
412               sizeof(compiler->functions[0]), sort_functions_cmp);
413 
414         /* Patch the function indexes. */
415         for (i = 0; i < compiler->num_functions; i++) {
416             WsFunctionHash *fh = ws_function_hash(compiler,
417                                                   compiler->functions[i].name);
418             compiler->functions[i].findex = i;
419             fh->findex = i;
420         }
421     }
422 
423     /* Linearize functions */
424     for (i = 0; i < compiler->num_functions; i++) {
425         WsFunction *func = &compiler->functions[i];
426 
427         ws_info(compiler, "linearizing function `%s'...", func->name);
428 
429         compiler->pool_asm = ws_f_create(100 * 1024);
430         if (compiler->pool_asm == NULL) {
431             result = WS_ERROR_OUT_OF_MEMORY;
432             goto out;
433         }
434 
435         compiler->next_label = 0;
436         compiler->asm_head = compiler->asm_tail = NULL;
437 
438         /* Create variables namespace. */
439         compiler->next_vindex = 0;
440         compiler->variables_hash = ws_variable_hash_create();
441         if (compiler->variables_hash == NULL) {
442             result = WS_ERROR_OUT_OF_MEMORY;
443             goto out;
444         }
445 
446         /* Define the formal arguments to the namespace. */
447         for (li = func->params->head; li; li = li->next) {
448             WsFormalParm *parm = li->data;
449 
450             ws_variable_define(compiler, parm->line, WS_FALSE, parm->name);
451         }
452 
453         WS_CHECK_COMPILE_ERROR();
454 
455         /* Linearize it. */
456         for (li = func->block->head; li; li = li->next)
457             ws_stmt_linearize(compiler, li->data);
458 
459         WS_CHECK_COMPILE_ERROR();
460 
461         /* Optimize symbolic assembler.  This function does nothing if
462            no optimizations were requested. */
463         ws_asm_optimize(compiler);
464 
465         /* Print the resulting symbolic assembler if requested. */
466         if (compiler->params.print_symbolic_assembler)
467             ws_asm_print(compiler);
468 
469         WS_CHECK_COMPILE_ERROR();
470 
471         /* Generate byte-code */
472 
473         ws_buffer_init(&compiler->byte_code);
474         ws_asm_linearize(compiler);
475 
476         WS_CHECK_COMPILE_ERROR();
477 
478         /* Disassemble the output if requested. */
479         if (compiler->params.print_assembler)
480             ws_asm_dasm(compiler, ws_buffer_ptr(&compiler->byte_code),
481                         ws_buffer_len(&compiler->byte_code));
482 
483         /* Calculate the number of local variables */
484         num_locals = compiler->next_vindex - func->params->num_items;
485 
486         /* Add the function to the byte-code module. */
487         if (!ws_bc_add_function(compiler->bc, &findex,
488                                 func->externp ? func->name : NULL,
489                                 func->params->num_items,
490                                 num_locals,
491                                 ws_buffer_len(&compiler->byte_code),
492                                 ws_buffer_ptr(&compiler->byte_code))) {
493             result = WS_ERROR_OUT_OF_MEMORY;
494             goto out;
495         }
496 
497         /* Cleanup and prepare for the next function. */
498 
499         ws_buffer_uninit(&compiler->byte_code);
500 
501         ws_hash_destroy(compiler->variables_hash);
502         compiler->variables_hash = NULL;
503 
504         ws_f_destroy(compiler->pool_asm);
505         compiler->pool_asm = NULL;
506     }
507 
508     /* Linearize the byte-code structure. */
509     if (!ws_bc_encode(compiler->bc, output_return, output_len_return))
510         result = WS_ERROR_OUT_OF_MEMORY;
511 
512 out:
513 
514     /* Cleanup. */
515 
516     ws_f_destroy(compiler->pool_stree);
517     compiler->pool_stree = NULL;
518 
519     ws_hash_destroy(compiler->pragma_use_hash);
520     compiler->pragma_use_hash = NULL;
521 
522     /* Free functions. */
523     for (i = 0; i < compiler->num_functions; i++)
524         ws_free(compiler->functions[i].name);
525     ws_free(compiler->functions);
526 
527     ws_hash_destroy(compiler->functions_hash);
528     compiler->functions_hash = NULL;
529 
530     ws_bc_free(compiler->bc);
531     compiler->bc = NULL;
532 
533     compiler->input = NULL;
534 
535     ws_f_destroy(compiler->pool_asm);
536     compiler->pool_asm = NULL;
537 
538     ws_hash_destroy(compiler->variables_hash);
539     compiler->variables_hash = NULL;
540 
541     ws_buffer_uninit(&compiler->byte_code);
542 
543     /* All done. */
544     return result;
545 }
546 
547 
std_io(const char * data,size_t len,void * context)548 static void std_io(const char *data, size_t len, void *context)
549 {
550     fwrite(data, 1, len, (FILE *) context);
551 }
552 
553 
sort_functions_cmp(const void * a,const void * b)554 static int sort_functions_cmp(const void *a, const void *b)
555 {
556     WsFunction *fa = (WsFunction *) a;
557     WsFunction *fb = (WsFunction *) b;
558 
559     if (fa->usage_count > fb->usage_count)
560         return -1;
561     if (fa->usage_count < fb->usage_count)
562         return 1;
563 
564     if (fa->findex < fb->findex)
565         return -1;
566 
567     return 1;
568 }
569