void ws_function(WsCompiler *compiler, WsBool externp, char *name, WsUInt32 line, WsList *params, WsList *block) { WsFunctionHash *hash; WsFunction *f = ws_realloc(compiler->functions, ((compiler->num_functions + 1) * sizeof(WsFunction))); if (f == NULL) { ws_free(name); ws_error_memory(compiler); return; } if (externp) compiler->num_extern_functions++; else compiler->num_local_functions++; compiler->functions = f; f = &compiler->functions[compiler->num_functions]; f->findex = compiler->num_functions++; f->externp = externp; f->name = name; f->line = line; f->params = params; f->block = block; /* Update the function name hash. */ hash = ws_function_hash(compiler, name); if (hash == NULL) { ws_error_memory(compiler); return; } if (hash->defined) { ws_src_error(compiler, line, "redefinition of `%s'", name); ws_src_error(compiler, compiler->functions[hash->findex].line, "`%s' previously defined here", name); return; } hash->defined = WS_TRUE; hash->findex = f->findex; }
void ws_expr_linearize(WsCompiler *compiler, WsExpression *expr) { WsListItem *li; WsAsmIns *ins; switch (expr->type) { case WS_EXPR_COMMA: /* Linearize left. */ ws_expr_linearize(compiler, expr->u.comma.left); /* Pop its result. */ ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, WS_ASM_POP)); /* Linearize right */ ws_expr_linearize(compiler, expr->u.comma.right); break; case WS_EXPR_ASSIGN: { WsNamespace *ns = ws_variable_lookup(compiler, expr->u.assign.identifier); if (ns == NULL) { /* Unknown identifier. */ ws_src_error(compiler, expr->line, "unknown variable `%s'", expr->u.symbol); return; } if (expr->u.assign.op == '=') { /* Evaluate the expression. */ ws_expr_linearize(compiler, expr->u.assign.expr); /* Store the value to the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_STORE_VAR, ns->vindex)); } else if (expr->u.assign.op == tADDA) { /* Linearize the expression. */ ws_expr_linearize(compiler, expr->u.assign.expr); /* Add it to the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_ADD_ASG, ns->vindex)); } else if (expr->u.assign.op == tSUBA) { /* Linearize the expression. */ ws_expr_linearize(compiler, expr->u.assign.expr); /* Substract it from the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_SUB_ASG, ns->vindex)); } else { /* Load the old value from the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_LOAD_VAR, ns->vindex)); /* Evaluate the expression. */ ws_expr_linearize(compiler, expr->u.assign.expr); /* Perform the operand. */ ins = NULL; switch (expr->u.assign.op) { case tMULA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_MUL); break; case tDIVA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_DIV); break; case tREMA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_REM); break; case tADDA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_ADD); break; case tSUBA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_SUB); break; case tLSHIFTA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_LSHIFT); break; case tRSSHIFTA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_RSSHIFT); break; case tRSZSHIFTA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_RSZSHIFT); break; case tANDA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_AND); break; case tXORA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_XOR); break; case tORA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_OR); break; case tIDIVA: ins = ws_asm_ins(compiler, expr->line, WS_ASM_IDIV); break; default: ws_fatal("ws_expr_linearize(): unknown assignment operand %x", expr->u.assign.op); break; } ws_asm_link(compiler, ins); /* Store the value to the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_STORE_VAR, ns->vindex)); } /* The value of the assignment expression is the value assigned. So, we must load the value from the variable. This would also be a good place for the `dup' operand but we lose since we don't have it. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_LOAD_VAR, ns->vindex)); } break; case WS_EXPR_CONDITIONAL: { WsAsmIns *l_else = ws_asm_label(compiler, expr->line); WsAsmIns *l_end = ws_asm_label(compiler, expr->line); /* Linearize condition. */ ws_expr_linearize(compiler, expr->u.conditional.e_cond); /* If the result if false, jump to the else-branch. */ ws_asm_link(compiler, ws_asm_branch(compiler, expr->line, WS_ASM_P_TJUMP, l_else)); /* Linearize the then-expression and jump out. */ ws_expr_linearize(compiler, expr->u.conditional.e_then); ws_asm_link(compiler, ws_asm_branch(compiler, expr->line, WS_ASM_P_JUMP, l_end)); /* The else-branch. */ ws_asm_link(compiler, l_else); ws_expr_linearize(compiler, expr->u.conditional.e_else); /* Insert the end label. */ ws_asm_link(compiler, l_end); } break; case WS_EXPR_LOGICAL: { WsAsmIns *l_out = ws_asm_label(compiler, expr->line); /* Linearize the left-hand size expression. */ ws_expr_linearize(compiler, expr->u.logical.left); /* Short-circuit check. The type of the logical expression is the short-circuit byte-code operand. */ ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, expr->u.logical.type)); ws_asm_link(compiler, ws_asm_branch(compiler, expr->line, WS_ASM_P_TJUMP, l_out)); /* Linearize the right-hand size expression. */ ws_expr_linearize(compiler, expr->u.logical.right); /* The result of a logical expression should be boolean. * Control statements do automatic conversion, but typeof() * does not. */ ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, WS_ASM_TOBOOL)); /* Insert the end label. */ ws_asm_link(compiler, l_out); } break; case WS_EXPR_BINARY: /* Linearize left and right. */ ws_expr_linearize(compiler, expr->u.binary.left); ws_expr_linearize(compiler, expr->u.binary.right); /* The type of the binary expression is the byte-code opcode. */ ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, expr->u.binary.type)); break; case WS_EXPR_UNARY: /* Linearize the expression. */ ws_expr_linearize(compiler, expr->u.unary.expr); /* The type of the unary expression is the byte-code opcode. */ ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, expr->u.unary.type)); break; case WS_EXPR_UNARY_VAR: { WsNamespace *ns = ws_variable_lookup(compiler, expr->u.unary_var.variable); if (ns == NULL) { /* An unknown identifier. */ ws_src_error(compiler, expr->line, "unknown variable `%s'", expr->u.unary_var.variable); return; } /* First, do the operation. */ if (expr->u.unary_var.addp) ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_INCR_VAR, ns->vindex)); else ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_DECR_VAR, ns->vindex)); /* Second, load the new value of the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_LOAD_VAR, ns->vindex)); } break; case WS_EXPR_POSTFIX_VAR: { WsNamespace *ns = ws_variable_lookup(compiler, expr->u.postfix_var.variable); if (ns == NULL) { /* An unknown identifier. */ ws_src_error(compiler, expr->line, "unknown variable `%s'", expr->u.postfix_var.variable); return; } /* First, load the old value of the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_LOAD_VAR, ns->vindex)); /* Second, do the operation. */ if (expr->u.unary_var.addp) ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_INCR_VAR, ns->vindex)); else ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_DECR_VAR, ns->vindex)); } break; case WS_EXPR_CALL: /* First, evaluate the arguments. */ for (li = expr->u.call.arguments->head; li; li = li->next) ws_expr_linearize(compiler, li->data); /* Second, emit the call instruction. */ switch (expr->u.call.type) { case ' ': /* LocalScriptFunctionCall */ { WsFunctionHash *f = ws_function_hash(compiler, expr->u.call.name); if (f == NULL || !f->defined) { ws_src_error(compiler, expr->line, "unknown local function `%s'", expr->u.call.name); return; } /* Check that the function is called with correct amount of arguments. */ if (expr->u.call.arguments->num_items != compiler->functions[f->findex].params->num_items) { ws_src_error(compiler, expr->line, "invalid amount of arguments for `%s': " "expected %u, got %u", expr->u.call.name, compiler->functions[f->findex].params->num_items, expr->u.call.arguments->num_items); return; } /* Emit assembler. */ ws_asm_link(compiler, ws_asm_call(compiler, expr->line, f->findex)); } break; case '#': /* ExternalScriptFunctionCall */ { WsPragmaUse *use = ws_hash_get(compiler->pragma_use_hash, expr->u.call.base); WsUInt16 findex; if (use == NULL) { ws_src_error(compiler, expr->line, "unknown external compilation unit `%s'", expr->u.call.base); return; } /* Insert the function name to the byte-code pool. */ if (!ws_bc_add_const_utf8_string( compiler->bc, &findex, (unsigned char *) expr->u.call.name, strlen(expr->u.call.name))) { ws_error_memory(compiler); return; } /* Emit assembler. */ ws_asm_link(compiler, ws_asm_call_url(compiler, expr->line, findex, use->urlindex, expr->u.call.arguments->num_items)); } break; case '.': /* LibraryFunctionCall */ { WsUInt16 lindex; WsUInt8 findex; WsUInt8 num_args; WsBool lindex_found; WsBool findex_found; if (!ws_stdlib_function(expr->u.call.base, expr->u.call.name, &lindex, &findex, &num_args, &lindex_found, &findex_found)) { if (!lindex_found) ws_src_error(compiler, expr->line, "unknown system library `%s'", expr->u.call.base); else ws_src_error(compiler, expr->line, "unknown library function `%s.%s'", expr->u.call.base, expr->u.call.name); return; } /* Check the argument count. */ if (expr->u.call.arguments->num_items != num_args) { ws_src_error(compiler, expr->line, "invalid amount of arguments for `%s.%s': " "expected %u, got %u", expr->u.call.base, expr->u.call.name, num_args, expr->u.call.arguments->num_items); return; } /* Emit assembler. */ ws_asm_link(compiler, ws_asm_call_lib(compiler, expr->line, findex, lindex)); } break; default: ws_fatal("ws_expr_linearize(): unknown call expression type %x", expr->u.call.type); break; } break; case WS_EXPR_SYMBOL: { WsNamespace *ns = ws_variable_lookup(compiler, expr->u.symbol); if (ns == NULL) { /* An unknown identifier. */ ws_src_error(compiler, expr->line, "unknown variable `%s'", expr->u.symbol); return; } /* Create a load instruction for the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, expr->line, WS_ASM_P_LOAD_VAR, ns->vindex)); } break; case WS_EXPR_CONST_INVALID: ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, WS_ASM_CONST_INVALID)); break; case WS_EXPR_CONST_TRUE: ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, WS_ASM_CONST_TRUE)); break; case WS_EXPR_CONST_FALSE: ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, WS_ASM_CONST_FALSE)); break; case WS_EXPR_CONST_INTEGER: if (expr->u.integer.ival == 0) ins = ws_asm_ins(compiler, expr->line, WS_ASM_CONST_0); else if (expr->u.integer.ival == 1 && expr->u.integer.sign == 1) ins = ws_asm_ins(compiler, expr->line, WS_ASM_CONST_1); else { WsUInt16 cindex; WsInt32 ival; if (expr->u.integer.sign >= 0) { if (expr->u.integer.ival > (WsUInt32) WS_INT32_MAX) ws_src_error(compiler, expr->line, "integer literal too large"); ival = expr->u.integer.ival; } else { if (expr->u.integer.ival > (WsUInt32) WS_INT32_MAX + 1) ws_src_error(compiler, expr->line, "integer too small"); ival = - (WsInt32) expr->u.integer.ival; } if (!ws_bc_add_const_int(compiler->bc, &cindex, ival)) { ws_error_memory(compiler); return; } ins = ws_asm_load_const(compiler, expr->line, cindex); } ws_asm_link(compiler, ins); break; case WS_EXPR_CONST_FLOAT: { WsUInt16 cindex; if (!ws_bc_add_const_float(compiler->bc, &cindex, expr->u.fval)) { ws_error_memory(compiler); return; } ws_asm_link(compiler, ws_asm_load_const(compiler, expr->line, cindex)); } break; case WS_EXPR_CONST_STRING: if (expr->u.string.len == 0) ins = ws_asm_ins(compiler, expr->line, WS_ASM_CONST_ES); else { WsUInt16 cindex; if (!ws_bc_add_const_utf8_string(compiler->bc, &cindex, expr->u.string.data, expr->u.string.len)) { ws_error_memory(compiler); return; } ins = ws_asm_load_const(compiler, expr->line, cindex); } ws_asm_link(compiler, ins); break; } }
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; }