/* ====================================================================
* The Kannel Software License, Version 1.0
*
* Copyright (c) 2001-2014 Kannel Group
* Copyright (c) 1998-2001 WapIT Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Kannel Group (http://www.kannel.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Kannel" and "Kannel Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact org@kannel.org.
*
* 5. Products derived from this software may not be called "Kannel",
* nor may "Kannel" appear in their name, without prior written
* permission of the Kannel Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Kannel Group. For more information on
* the Kannel Group, please see .
*
* Portions of this software are based upon software originally written at
* WapIT Ltd., Helsinki, Finland for the Kannel project.
*/
/*
*
* ws.c
*
* Author: Markku Rossi
*
* Copyright (c) 1999-2000 WAPIT OY LTD.
* All rights reserved.
*
* Public entry points to the WMLScript compiler and the main compile
* function.
*
*/
#include "wsint.h"
#include "ws.h"
#include "wsstree.h"
#include "wsasm.h"
/********************* Types and definitions ****************************/
#define WS_CHECK_COMPILE_ERROR() \
do { \
if (compiler->errors != 0) { \
if (compiler->errors & WS_ERROR_B_MEMORY) \
result = WS_ERROR_OUT_OF_MEMORY; \
else if (compiler->errors & WS_ERROR_B_SYNTAX) \
result = WS_ERROR_SYNTAX; \
else if (compiler->errors & WS_ERROR_B_SEMANTIC) \
result = WS_ERROR_SEMANTIC; \
else \
/* This shouldn't happen. */ \
result = WS_ERROR; \
goto out; \
} \
} while (0);
/********************* Static variables *********************************/
/* Human readable names for the result codes. */
static struct
{
WsResult code;
char *description;
} result_codes[] = {
{ WS_OK, "success" },
{ WS_ERROR_OUT_OF_MEMORY, "out of memory" },
{ WS_ERROR_SYNTAX, "syntax error" },
{ WS_ERROR_SEMANTIC, "compile error" },
{ WS_ERROR_IO, "IO error" },
{ WS_ERROR, "error" },
{ 0, NULL },
};
/********************* Prototypes for static functions ******************/
/* Compile the input stream `input' with the compiler `compiler' and
return the byte-code in `output_return' and its length in
`output_len_return'. The function returns a WsResult error code
describing the success of the compilation. */
static WsResult compile_stream(WsCompilerPtr compiler,
const char *input_name, WsStream *input,
unsigned char **output_return,
size_t *output_len_return);
/* The default I/O function to send the output to stdout and stderr.
The argument `context' must be a `FILE *' file to which the output
is written. */
static void std_io(const char *data, size_t len, void *context);
/* A comparison function for functions entries when sorting them by
their usage counts. The function is stable maintaining their
original order if their usage counts are equal. */
static int sort_functions_cmp(const void *a, const void *b);
/********************* Global functions *********************************/
WsCompilerPtr ws_create(WsCompilerParams *params)
{
WsCompilerPtr compiler = ws_calloc(1, sizeof(*compiler));
if (compiler == NULL)
return NULL;
/* Store user params if specified. */
if (params)
compiler->params = *params;
/* Basic initialization. */
compiler->magic = COMPILER_MAGIC;
if (compiler->params.stdout_cb == NULL) {
compiler->params.stdout_cb = std_io;
compiler->params.stdout_cb_context = stdout;
}
if (compiler->params.stderr_cb == NULL) {
compiler->params.stderr_cb = std_io;
compiler->params.stderr_cb_context = stderr;
}
return compiler;
}
void ws_destroy(WsCompilerPtr compiler)
{
if (compiler == NULL)
return;
ws_free(compiler);
#if WS_MEM_DEBUG
if (ws_has_leaks())
ws_dump_blocks();
#endif /* WS_MEM_DEBUG */
}
WsResult ws_compile_file(WsCompilerPtr compiler, const char *input_name,
FILE *input, FILE *output)
{
WsResult result;
WsStream *stream;
unsigned char *bc;
size_t bc_len;
/* Initialize the input stream. */
stream = ws_stream_new_file(input, WS_FALSE, WS_FALSE);
if (stream == NULL)
return WS_ERROR_OUT_OF_MEMORY;
result = compile_stream(compiler, input_name, stream, &bc, &bc_len);
ws_stream_close(stream);
if (result == WS_OK) {
/* Store the result to the file. */
if (fwrite(bc, 1, bc_len, output) != bc_len)
result = WS_ERROR_IO;
ws_bc_data_free(bc);
}
return result;
}
WsResult 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)
{
WsResult result;
WsStream *stream;
/* Initialize the input stream. */
stream = ws_stream_new_data_input(input, input_len);
if (stream == NULL)
return WS_ERROR_OUT_OF_MEMORY;
result = compile_stream(compiler, input_name, stream, output_return,
output_len_return);
ws_stream_close(stream);
return result;
}
void ws_free_byte_code(unsigned char *byte_code)
{
ws_bc_data_free(byte_code);
}
const char *ws_result_to_string(WsResult result)
{
int i;
for (i = 0; result_codes[i].description; i++) {
if (result_codes[i].code == result)
return result_codes[i].description;
}
return "unknown result code";
}
/********************* Lexer's memory handling helpers ******************/
WsBool ws_lexer_register_block(WsCompiler *compiler, void *ptr)
{
void **n;
if (ptr == NULL)
return WS_TRUE;
n = ws_realloc(compiler->lexer_active_list,
((compiler->lexer_active_list_size + 1) * sizeof(void *)));
if (n == NULL)
return WS_FALSE;
compiler->lexer_active_list = n;
compiler->lexer_active_list[compiler->lexer_active_list_size++] = ptr;
return WS_TRUE;
}
WsBool ws_lexer_register_utf8(WsCompiler *compiler, WsUtf8String *string)
{
if (!ws_lexer_register_block(compiler, string))
return WS_FALSE;
if (!ws_lexer_register_block(compiler, string->data)) {
compiler->lexer_active_list_size--;
return WS_FALSE;
}
return WS_TRUE;
}
void ws_lexer_free_block(WsCompiler *compiler, void *ptr)
{
int i;
if (ptr == NULL)
return;
for (i = compiler->lexer_active_list_size - 1; i >= 0; i--) {
if (compiler->lexer_active_list[i] == ptr) {
memmove(&compiler->lexer_active_list[i],
&compiler->lexer_active_list[i + 1],
(compiler->lexer_active_list_size - i - 1) * sizeof(void *));
compiler->lexer_active_list_size--;
ws_free(ptr);
return;
}
}
ws_fatal("ws_lexer_free_block(): unknown block 0x%lx",
(unsigned long) ptr);
}
void ws_lexer_free_utf8(WsCompiler *compiler, WsUtf8String *string)
{
if (string == NULL)
return;
ws_lexer_free_block(compiler, string->data);
ws_lexer_free_block(compiler, string);
}
/********************* Static functions *********************************/
static WsResult compile_stream(WsCompilerPtr compiler, const char *input_name,
WsStream *input, unsigned char **output_return,
size_t *output_len_return)
{
WsResult result = WS_OK;
WsUInt32 i;
WsListItem *li;
WsUInt8 findex;
WsUInt8 num_locals;
WsBcStringEncoding string_encoding = WS_BC_STRING_ENC_UTF8;
/* Initialize the compiler context. */
compiler->linenum = 1;
compiler->input_name = input_name;
compiler->num_errors = 0;
compiler->num_warnings = 0;
compiler->num_extern_functions = 0;
compiler->num_local_functions = 0;
compiler->errors = 0;
compiler->last_syntax_error_line = 0;
/* Allocate fast-malloc pool for the syntax tree. */
compiler->pool_stree = ws_f_create(1024 * 1024);
if (compiler->pool_stree == NULL) {
result = WS_ERROR_OUT_OF_MEMORY;
goto out;
}
/* Allocate hash tables. */
compiler->pragma_use_hash = ws_pragma_use_hash_create();
if (compiler->pragma_use_hash == NULL) {
result = WS_ERROR_OUT_OF_MEMORY;
goto out;
}
compiler->functions_hash = ws_function_hash_create();
if (compiler->functions_hash == NULL) {
result = WS_ERROR_OUT_OF_MEMORY;
goto out;
}
/* Allocate a byte-code module. */
if (compiler->params.use_latin1_strings)
string_encoding = WS_BC_STRING_ENC_ISO_8859_1;
compiler->bc = ws_bc_alloc(string_encoding);
if (compiler->bc == NULL) {
result = WS_ERROR_OUT_OF_MEMORY;
goto out;
}
/* Save the input stream. */
compiler->input = input;
/* Parse the input. */
#if WS_DEBUG
global_compiler = compiler;
#endif /* WS_DEBUG */
ws_yy_parse(compiler);
/* Free all lexer's active not freed blocks. If we have any blocks
on the used list, our compilation was not successful. */
{
size_t j;
for (j = 0; j < compiler->lexer_active_list_size; j++)
ws_free(compiler->lexer_active_list[j]);
ws_free(compiler->lexer_active_list);
compiler->lexer_active_list = NULL;
}
WS_CHECK_COMPILE_ERROR();
/* Sort functions if allowed and it helps. */
if (!compiler->params.no_opt_sort_bc_functions
&& compiler->num_functions > 7) {
WsUInt32 i;
ws_info(compiler, "optimize: sorting functions");
/* Fetch the usage counts from the functions hash. */
for (i = 0; i < compiler->num_functions; i++) {
WsFunctionHash *fh = ws_function_hash(compiler,
compiler->functions[i].name);
compiler->functions[i].usage_count = fh->usage_count;
}
/* Sort functions. */
qsort(compiler->functions, compiler->num_functions,
sizeof(compiler->functions[0]), sort_functions_cmp);
/* Patch the function indexes. */
for (i = 0; i < compiler->num_functions; i++) {
WsFunctionHash *fh = ws_function_hash(compiler,
compiler->functions[i].name);
compiler->functions[i].findex = i;
fh->findex = i;
}
}
/* Linearize functions */
for (i = 0; i < compiler->num_functions; i++) {
WsFunction *func = &compiler->functions[i];
ws_info(compiler, "linearizing function `%s'...", func->name);
compiler->pool_asm = ws_f_create(100 * 1024);
if (compiler->pool_asm == NULL) {
result = WS_ERROR_OUT_OF_MEMORY;
goto out;
}
compiler->next_label = 0;
compiler->asm_head = compiler->asm_tail = NULL;
/* Create variables namespace. */
compiler->next_vindex = 0;
compiler->variables_hash = ws_variable_hash_create();
if (compiler->variables_hash == NULL) {
result = WS_ERROR_OUT_OF_MEMORY;
goto out;
}
/* Define the formal arguments to the namespace. */
for (li = func->params->head; li; li = li->next) {
WsFormalParm *parm = li->data;
ws_variable_define(compiler, parm->line, WS_FALSE, parm->name);
}
WS_CHECK_COMPILE_ERROR();
/* Linearize it. */
for (li = func->block->head; li; li = li->next)
ws_stmt_linearize(compiler, li->data);
WS_CHECK_COMPILE_ERROR();
/* Optimize symbolic assembler. This function does nothing if
no optimizations were requested. */
ws_asm_optimize(compiler);
/* Print the resulting symbolic assembler if requested. */
if (compiler->params.print_symbolic_assembler)
ws_asm_print(compiler);
WS_CHECK_COMPILE_ERROR();
/* Generate byte-code */
ws_buffer_init(&compiler->byte_code);
ws_asm_linearize(compiler);
WS_CHECK_COMPILE_ERROR();
/* Disassemble the output if requested. */
if (compiler->params.print_assembler)
ws_asm_dasm(compiler, ws_buffer_ptr(&compiler->byte_code),
ws_buffer_len(&compiler->byte_code));
/* Calculate the number of local variables */
num_locals = compiler->next_vindex - func->params->num_items;
/* Add the function to the byte-code module. */
if (!ws_bc_add_function(compiler->bc, &findex,
func->externp ? func->name : NULL,
func->params->num_items,
num_locals,
ws_buffer_len(&compiler->byte_code),
ws_buffer_ptr(&compiler->byte_code))) {
result = WS_ERROR_OUT_OF_MEMORY;
goto out;
}
/* Cleanup and prepare for the next function. */
ws_buffer_uninit(&compiler->byte_code);
ws_hash_destroy(compiler->variables_hash);
compiler->variables_hash = NULL;
ws_f_destroy(compiler->pool_asm);
compiler->pool_asm = NULL;
}
/* Linearize the byte-code structure. */
if (!ws_bc_encode(compiler->bc, output_return, output_len_return))
result = WS_ERROR_OUT_OF_MEMORY;
out:
/* Cleanup. */
ws_f_destroy(compiler->pool_stree);
compiler->pool_stree = NULL;
ws_hash_destroy(compiler->pragma_use_hash);
compiler->pragma_use_hash = NULL;
/* Free functions. */
for (i = 0; i < compiler->num_functions; i++)
ws_free(compiler->functions[i].name);
ws_free(compiler->functions);
ws_hash_destroy(compiler->functions_hash);
compiler->functions_hash = NULL;
ws_bc_free(compiler->bc);
compiler->bc = NULL;
compiler->input = NULL;
ws_f_destroy(compiler->pool_asm);
compiler->pool_asm = NULL;
ws_hash_destroy(compiler->variables_hash);
compiler->variables_hash = NULL;
ws_buffer_uninit(&compiler->byte_code);
/* All done. */
return result;
}
static void std_io(const char *data, size_t len, void *context)
{
fwrite(data, 1, len, (FILE *) context);
}
static int sort_functions_cmp(const void *a, const void *b)
{
WsFunction *fa = (WsFunction *) a;
WsFunction *fb = (WsFunction *) b;
if (fa->usage_count > fb->usage_count)
return -1;
if (fa->usage_count < fb->usage_count)
return 1;
if (fa->findex < fb->findex)
return -1;
return 1;
}