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