wine/dlls/jscript/compile.c
Gabriel Ivăncescu 59372aa98c jscript: Start from the last argument when adding them to named locals.
This is needed for duplicated argument names, as the last arg will shadow
all the prior ones when it comes to name lookup.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
2023-06-21 18:54:49 +02:00

2777 lines
78 KiB
C

/*
* Copyright 2011 Jacek Caban for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <math.h>
#include <assert.h>
#include "jscript.h"
#include "engine.h"
#include "parser.h"
#include "wine/rbtree.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(jscript);
WINE_DECLARE_DEBUG_CHANNEL(jscript_disas);
typedef struct _statement_ctx_t {
unsigned stack_use;
BOOL using_scope;
BOOL using_except;
unsigned break_label;
unsigned continue_label;
const labelled_statement_t *labelled_stat;
unsigned int scope_index;
BOOL block_scope;
BOOL scope_has_functions;
struct _statement_ctx_t *next;
} statement_ctx_t;
typedef struct {
struct wine_rb_entry entry;
BSTR name;
int ref;
} function_local_t;
typedef struct _compiler_ctx_t {
parser_ctx_t *parser;
bytecode_t *code;
BOOL from_eval;
unsigned code_off;
unsigned code_size;
unsigned *labels;
unsigned labels_size;
unsigned labels_cnt;
struct
{
struct wine_rb_tree locals;
unsigned int locals_cnt;
unsigned int *ref_index;
}
*local_scopes;
unsigned local_scope_count;
unsigned local_scope_size;
statement_ctx_t *stat_ctx;
function_code_t *func;
unsigned loc;
function_expression_t *func_head;
function_expression_t *func_tail;
function_expression_t *current_function_expr;
heap_pool_t heap;
} compiler_ctx_t;
static const struct {
const char *op_str;
instr_arg_type_t arg1_type;
instr_arg_type_t arg2_type;
} instr_info[] = {
#define X(n,a,b,c) {#n,b,c},
OP_LIST
#undef X
};
static void dump_instr_arg(instr_arg_type_t type, instr_arg_t *arg)
{
switch(type) {
case ARG_STR:
TRACE_(jscript_disas)("\t%s", debugstr_jsstr(arg->str));
break;
case ARG_BSTR:
TRACE_(jscript_disas)("\t%s", debugstr_wn(arg->bstr, SysStringLen(arg->bstr)));
break;
case ARG_INT:
TRACE_(jscript_disas)("\t%d", arg->uint);
break;
case ARG_UINT:
case ARG_ADDR:
TRACE_(jscript_disas)("\t%u", arg->uint);
break;
case ARG_FUNC:
case ARG_NONE:
break;
DEFAULT_UNREACHABLE;
}
}
static void dump_code(compiler_ctx_t *ctx, unsigned off)
{
instr_t *instr;
for(instr = ctx->code->instrs+off; instr < ctx->code->instrs+ctx->code_off; instr++) {
TRACE_(jscript_disas)("%d:\t%s", (int)(instr-ctx->code->instrs), instr_info[instr->op].op_str);
if(instr_info[instr->op].arg1_type == ARG_DBL) {
TRACE_(jscript_disas)("\t%lf", instr->u.dbl);
}else {
dump_instr_arg(instr_info[instr->op].arg1_type, instr->u.arg);
dump_instr_arg(instr_info[instr->op].arg2_type, instr->u.arg+1);
}
TRACE_(jscript_disas)("\n");
}
}
static HRESULT compile_expression(compiler_ctx_t*,expression_t*,BOOL);
static HRESULT compile_statement(compiler_ctx_t*,statement_ctx_t*,statement_t*);
static int function_local_cmp(const void *key, const struct wine_rb_entry *entry)
{
function_local_t *local = WINE_RB_ENTRY_VALUE(entry, function_local_t, entry);
return wcscmp(key, local->name);
}
static BOOL alloc_local_scope(compiler_ctx_t *ctx, unsigned int *scope_index)
{
unsigned int scope, new_size;
void *new_alloc;
scope = ctx->local_scope_count++;
if (scope == ctx->local_scope_size)
{
new_size = max(1, ctx->local_scope_size * 2);
if (!(new_alloc = realloc(ctx->local_scopes, new_size * sizeof(*ctx->local_scopes))))
return FALSE;
ctx->local_scopes = new_alloc;
ctx->local_scope_size = new_size;
}
ctx->local_scopes[scope].locals_cnt = 0;
ctx->local_scopes[scope].ref_index = scope_index;
wine_rb_init(&ctx->local_scopes[scope].locals, function_local_cmp);
*scope_index = scope;
return TRUE;
}
static void remove_local_scope(compiler_ctx_t *ctx, unsigned int scope_index)
{
unsigned int i;
assert(scope_index < ctx->local_scope_count);
--ctx->local_scope_count;
assert(scope_index == *ctx->local_scopes[scope_index].ref_index);
*ctx->local_scopes[scope_index].ref_index = 0;
memmove(&ctx->local_scopes[scope_index], &ctx->local_scopes[scope_index + 1],
sizeof(*ctx->local_scopes) * (ctx->local_scope_count - scope_index));
for (i = scope_index; i < ctx->local_scope_count; ++i)
--*ctx->local_scopes[i].ref_index;
}
static inline void *compiler_alloc(bytecode_t *code, size_t size)
{
return heap_pool_alloc(&code->heap, size);
}
jsstr_t *compiler_alloc_string_len(compiler_ctx_t *ctx, const WCHAR *str, unsigned len)
{
jsstr_t *new_str;
if(!ctx->code->str_pool_size) {
ctx->code->str_pool = malloc(8 * sizeof(jsstr_t*));
if(!ctx->code->str_pool)
return NULL;
ctx->code->str_pool_size = 8;
}else if(ctx->code->str_pool_size == ctx->code->str_cnt) {
jsstr_t **new_pool;
new_pool = realloc(ctx->code->str_pool, ctx->code->str_pool_size*2*sizeof(jsstr_t*));
if(!new_pool)
return NULL;
ctx->code->str_pool = new_pool;
ctx->code->str_pool_size *= 2;
}
new_str = jsstr_alloc_len(str, len);
if(!new_str)
return NULL;
ctx->code->str_pool[ctx->code->str_cnt++] = new_str;
return new_str;
}
static jsstr_t *compiler_alloc_string(compiler_ctx_t *ctx, const WCHAR *str)
{
return compiler_alloc_string_len(ctx, str, lstrlenW(str));
}
static BOOL ensure_bstr_slot(compiler_ctx_t *ctx)
{
if(!ctx->code->bstr_pool_size) {
ctx->code->bstr_pool = malloc(8 * sizeof(BSTR));
if(!ctx->code->bstr_pool)
return FALSE;
ctx->code->bstr_pool_size = 8;
}else if(ctx->code->bstr_pool_size == ctx->code->bstr_cnt) {
BSTR *new_pool;
new_pool = realloc(ctx->code->bstr_pool, ctx->code->bstr_pool_size*2*sizeof(BSTR));
if(!new_pool)
return FALSE;
ctx->code->bstr_pool = new_pool;
ctx->code->bstr_pool_size *= 2;
}
return TRUE;
}
static BSTR compiler_alloc_bstr(compiler_ctx_t *ctx, const WCHAR *str)
{
if(!ensure_bstr_slot(ctx))
return NULL;
ctx->code->bstr_pool[ctx->code->bstr_cnt] = SysAllocString(str);
if(!ctx->code->bstr_pool[ctx->code->bstr_cnt])
return NULL;
return ctx->code->bstr_pool[ctx->code->bstr_cnt++];
}
static BSTR compiler_alloc_bstr_len(compiler_ctx_t *ctx, const WCHAR *str, size_t len)
{
if(!ensure_bstr_slot(ctx))
return NULL;
ctx->code->bstr_pool[ctx->code->bstr_cnt] = SysAllocStringLen(str, len);
if(!ctx->code->bstr_pool[ctx->code->bstr_cnt])
return NULL;
return ctx->code->bstr_pool[ctx->code->bstr_cnt++];
}
void set_compiler_loc(compiler_ctx_t *ctx, unsigned loc)
{
ctx->loc = loc;
}
static unsigned push_instr(compiler_ctx_t *ctx, jsop_t op)
{
assert(ctx->code_size >= ctx->code_off);
if(ctx->code_size == ctx->code_off) {
instr_t *new_instrs;
new_instrs = realloc(ctx->code->instrs, ctx->code_size*2*sizeof(instr_t));
if(!new_instrs)
return 0;
ctx->code->instrs = new_instrs;
ctx->code_size *= 2;
}
ctx->code->instrs[ctx->code_off].op = op;
ctx->code->instrs[ctx->code_off].loc = ctx->loc;
return ctx->code_off++;
}
static inline instr_t *instr_ptr(compiler_ctx_t *ctx, unsigned off)
{
assert(off < ctx->code_off);
return ctx->code->instrs + off;
}
static HRESULT push_instr_int(compiler_ctx_t *ctx, jsop_t op, LONG arg)
{
unsigned instr;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.arg->lng = arg;
return S_OK;
}
static HRESULT push_instr_str(compiler_ctx_t *ctx, jsop_t op, jsstr_t *str)
{
unsigned instr;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.arg->str = str;
return S_OK;
}
static HRESULT push_instr_str_uint(compiler_ctx_t *ctx, jsop_t op, jsstr_t *str, unsigned arg2)
{
unsigned instr;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.arg[0].str = str;
instr_ptr(ctx, instr)->u.arg[1].uint = arg2;
return S_OK;
}
static HRESULT push_instr_bstr(compiler_ctx_t *ctx, jsop_t op, const WCHAR *arg)
{
unsigned instr;
WCHAR *str;
str = compiler_alloc_bstr(ctx, arg);
if(!str)
return E_OUTOFMEMORY;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.arg->bstr = str;
return S_OK;
}
static HRESULT push_instr_bstr_uint(compiler_ctx_t *ctx, jsop_t op, const WCHAR *arg1, unsigned arg2)
{
unsigned instr;
WCHAR *str;
str = compiler_alloc_bstr(ctx, arg1);
if(!str)
return E_OUTOFMEMORY;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.arg[0].bstr = str;
instr_ptr(ctx, instr)->u.arg[1].uint = arg2;
return S_OK;
}
static HRESULT push_instr_uint_str(compiler_ctx_t *ctx, jsop_t op, unsigned arg1, const WCHAR *arg2)
{
unsigned instr;
jsstr_t *str;
str = compiler_alloc_string(ctx, arg2);
if(!str)
return E_OUTOFMEMORY;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.arg[0].uint = arg1;
instr_ptr(ctx, instr)->u.arg[1].str = str;
return S_OK;
}
static HRESULT push_instr_double(compiler_ctx_t *ctx, jsop_t op, double arg)
{
unsigned instr;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.dbl = arg;
return S_OK;
}
static inline void set_arg_uint(compiler_ctx_t *ctx, unsigned instr, unsigned arg)
{
instr_ptr(ctx, instr)->u.arg->uint = arg;
}
static HRESULT push_instr_uint(compiler_ctx_t *ctx, jsop_t op, unsigned arg)
{
unsigned instr;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
set_arg_uint(ctx, instr, arg);
return S_OK;
}
static HRESULT compile_binary_expression(compiler_ctx_t *ctx, binary_expression_t *expr, jsop_t op)
{
HRESULT hres;
hres = compile_expression(ctx, expr->expression1, TRUE);
if(FAILED(hres))
return hres;
hres = compile_expression(ctx, expr->expression2, TRUE);
if(FAILED(hres))
return hres;
return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY;
}
static HRESULT compile_unary_expression(compiler_ctx_t *ctx, unary_expression_t *expr, jsop_t op)
{
HRESULT hres;
hres = compile_expression(ctx, expr->expression, TRUE);
if(FAILED(hres))
return hres;
return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY;
}
/* ECMA-262 3rd Edition 11.2.1 */
static HRESULT compile_member_expression(compiler_ctx_t *ctx, member_expression_t *expr)
{
HRESULT hres;
hres = compile_expression(ctx, expr->expression, TRUE);
if(FAILED(hres))
return hres;
return push_instr_bstr(ctx, OP_member, expr->identifier);
}
#define LABEL_FLAG 0x80000000
static unsigned alloc_label(compiler_ctx_t *ctx)
{
if(!ctx->labels_size) {
ctx->labels = malloc(8 * sizeof(*ctx->labels));
if(!ctx->labels)
return 0;
ctx->labels_size = 8;
}else if(ctx->labels_size == ctx->labels_cnt) {
unsigned *new_labels;
new_labels = realloc(ctx->labels, 2*ctx->labels_size*sizeof(*ctx->labels));
if(!new_labels)
return 0;
ctx->labels = new_labels;
ctx->labels_size *= 2;
}
return ctx->labels_cnt++ | LABEL_FLAG;
}
static void label_set_addr(compiler_ctx_t *ctx, unsigned label)
{
assert(label & LABEL_FLAG);
ctx->labels[label & ~LABEL_FLAG] = ctx->code_off;
}
static inline BOOL is_memberid_expr(expression_type_t type)
{
return type == EXPR_IDENT || type == EXPR_MEMBER || type == EXPR_ARRAY;
}
static BOOL bind_local(compiler_ctx_t *ctx, const WCHAR *identifier, int *ret_ref)
{
statement_ctx_t *iter;
local_ref_t *ref;
for(iter = ctx->stat_ctx; iter; iter = iter->next) {
if(iter->using_scope)
{
if (!iter->block_scope)
return FALSE;
if ((ref = lookup_local(ctx->func, identifier, iter->scope_index)))
{
*ret_ref = ref->ref;
return TRUE;
}
}
}
ref = lookup_local(ctx->func, identifier, 0);
if(!ref)
return FALSE;
*ret_ref = ref->ref;
return TRUE;
}
static HRESULT emit_identifier_ref(compiler_ctx_t *ctx, const WCHAR *identifier, unsigned flags)
{
int local_ref;
if(bind_local(ctx, identifier, &local_ref))
return push_instr_int(ctx, OP_local_ref, local_ref);
return push_instr_bstr_uint(ctx, OP_identid, identifier, flags);
}
static HRESULT emit_identifier(compiler_ctx_t *ctx, const WCHAR *identifier)
{
int local_ref;
if(bind_local(ctx, identifier, &local_ref))
return push_instr_int(ctx, OP_local, local_ref);
return push_instr_bstr(ctx, OP_ident, identifier);
}
static HRESULT emit_member_expression(compiler_ctx_t *ctx, expression_t *expr)
{
HRESULT hres;
if(expr->type == EXPR_ARRAY) {
binary_expression_t *array_expr = (binary_expression_t*)expr;
hres = compile_expression(ctx, array_expr->expression1, TRUE);
if(FAILED(hres))
return hres;
hres = compile_expression(ctx, array_expr->expression2, TRUE);
if(FAILED(hres))
return hres;
if(!push_instr(ctx, OP_to_string))
return E_OUTOFMEMORY;
}else {
member_expression_t *member_expr = (member_expression_t*)expr;
jsstr_t *jsstr;
assert(expr->type == EXPR_MEMBER);
hres = compile_expression(ctx, member_expr->expression, TRUE);
if(FAILED(hres))
return hres;
jsstr = compiler_alloc_string(ctx, member_expr->identifier);
if(!jsstr)
return E_OUTOFMEMORY;
hres = push_instr_str(ctx, OP_str, jsstr);
if(FAILED(hres))
return hres;
}
return S_OK;
}
static void push_compiler_statement_ctx(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx)
{
if (stat_ctx)
{
stat_ctx->next = ctx->stat_ctx;
ctx->stat_ctx = stat_ctx;
}
}
static void pop_compiler_statement_ctx(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx)
{
if (stat_ctx)
{
assert(ctx->stat_ctx == stat_ctx);
ctx->stat_ctx = stat_ctx->next;
}
}
static HRESULT compile_memberid_expression(compiler_ctx_t *ctx, expression_t *expr, unsigned flags)
{
HRESULT hres;
if(expr->type == EXPR_IDENT) {
identifier_expression_t *ident_expr = (identifier_expression_t*)expr;
return emit_identifier_ref(ctx, ident_expr->identifier, flags);
}
hres = emit_member_expression(ctx, expr);
if(FAILED(hres))
return hres;
return push_instr_uint(ctx, OP_memberid, flags);
}
static HRESULT compile_increment_expression(compiler_ctx_t *ctx, unary_expression_t *expr, jsop_t op, int n)
{
HRESULT hres;
if(!is_memberid_expr(expr->expression->type)) {
hres = compile_expression(ctx, expr->expression, TRUE);
if(FAILED(hres))
return hres;
return push_instr_uint(ctx, OP_throw_ref, JS_E_ILLEGAL_ASSIGN);
}
hres = compile_memberid_expression(ctx, expr->expression, fdexNameEnsure);
if(FAILED(hres))
return hres;
return push_instr_int(ctx, op, n);
}
/* ECMA-262 3rd Edition 11.14 */
static HRESULT compile_comma_expression(compiler_ctx_t *ctx, binary_expression_t *expr, BOOL emit_ret)
{
HRESULT hres;
hres = compile_expression(ctx, expr->expression1, FALSE);
if(FAILED(hres))
return hres;
return compile_expression(ctx, expr->expression2, emit_ret);
}
/* ECMA-262 3rd Edition 11.11 */
static HRESULT compile_logical_expression(compiler_ctx_t *ctx, binary_expression_t *expr, jsop_t op)
{
unsigned instr;
HRESULT hres;
hres = compile_expression(ctx, expr->expression1, TRUE);
if(FAILED(hres))
return hres;
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
hres = compile_expression(ctx, expr->expression2, TRUE);
if(FAILED(hres))
return hres;
set_arg_uint(ctx, instr, ctx->code_off);
return S_OK;
}
/* ECMA-262 3rd Edition 11.12 */
static HRESULT compile_conditional_expression(compiler_ctx_t *ctx, conditional_expression_t *expr)
{
unsigned jmp_false, jmp_end;
HRESULT hres;
hres = compile_expression(ctx, expr->expression, TRUE);
if(FAILED(hres))
return hres;
jmp_false = push_instr(ctx, OP_cnd_z);
if(!jmp_false)
return E_OUTOFMEMORY;
hres = compile_expression(ctx, expr->true_expression, TRUE);
if(FAILED(hres))
return hres;
jmp_end = push_instr(ctx, OP_jmp);
if(!jmp_end)
return E_OUTOFMEMORY;
set_arg_uint(ctx, jmp_false, ctx->code_off);
hres = push_instr_uint(ctx, OP_pop, 1);
if(FAILED(hres))
return hres;
hres = compile_expression(ctx, expr->false_expression, TRUE);
if(FAILED(hres))
return hres;
set_arg_uint(ctx, jmp_end, ctx->code_off);
return S_OK;
}
static HRESULT compile_new_expression(compiler_ctx_t *ctx, call_expression_t *expr)
{
unsigned arg_cnt = 0;
argument_t *arg;
HRESULT hres;
hres = compile_expression(ctx, expr->expression, TRUE);
if(FAILED(hres))
return hres;
for(arg = expr->argument_list; arg; arg = arg->next) {
hres = compile_expression(ctx, arg->expr, TRUE);
if(FAILED(hres))
return hres;
arg_cnt++;
}
hres = push_instr_uint(ctx, OP_new, arg_cnt);
if(FAILED(hres))
return hres;
hres = push_instr_uint(ctx, OP_pop, arg_cnt+1);
if(FAILED(hres))
return hres;
return push_instr(ctx, OP_push_acc) ? S_OK : E_OUTOFMEMORY;
}
static HRESULT compile_call_expression(compiler_ctx_t *ctx, call_expression_t *expr, BOOL emit_ret)
{
unsigned arg_cnt = 0, extra_args = 0;
HRESULT hres = S_OK;
argument_t *arg;
unsigned instr;
jsop_t op;
if(is_memberid_expr(expr->expression->type)) {
if(expr->expression->type == EXPR_IDENT && !wcscmp(((identifier_expression_t*)expr->expression)->identifier, L"eval"))
op = OP_call_eval;
else {
op = OP_call_member;
extra_args = 2;
hres = compile_memberid_expression(ctx, expr->expression, 0);
}
}else {
op = OP_call;
extra_args = 1;
hres = compile_expression(ctx, expr->expression, TRUE);
}
if(FAILED(hres))
return hres;
for(arg = expr->argument_list; arg; arg = arg->next) {
hres = compile_expression(ctx, arg->expr, TRUE);
if(FAILED(hres))
return hres;
arg_cnt++;
}
instr = push_instr(ctx, op);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.arg[0].uint = arg_cnt;
instr_ptr(ctx, instr)->u.arg[1].lng = emit_ret;
hres = push_instr_uint(ctx, OP_pop, arg_cnt + extra_args);
if(FAILED(hres))
return hres;
return !emit_ret || push_instr(ctx, OP_push_acc) ? S_OK : E_OUTOFMEMORY;
}
static HRESULT compile_delete_expression(compiler_ctx_t *ctx, unary_expression_t *expr)
{
HRESULT hres;
switch(expr->expression->type) {
case EXPR_ARRAY: {
binary_expression_t *array_expr = (binary_expression_t*)expr->expression;
hres = compile_expression(ctx, array_expr->expression1, TRUE);
if(FAILED(hres))
return hres;
hres = compile_expression(ctx, array_expr->expression2, TRUE);
if(FAILED(hres))
return hres;
if(!push_instr(ctx, OP_delete))
return E_OUTOFMEMORY;
break;
}
case EXPR_MEMBER: {
member_expression_t *member_expr = (member_expression_t*)expr->expression;
jsstr_t *jsstr;
hres = compile_expression(ctx, member_expr->expression, TRUE);
if(FAILED(hres))
return hres;
/* FIXME: Potential optimization */
jsstr = compiler_alloc_string(ctx, member_expr->identifier);
if(!jsstr)
return E_OUTOFMEMORY;
hres = push_instr_str(ctx, OP_str, jsstr);
if(FAILED(hres))
return hres;
if(!push_instr(ctx, OP_delete))
return E_OUTOFMEMORY;
break;
}
case EXPR_IDENT:
return push_instr_bstr(ctx, OP_delete_ident, ((identifier_expression_t*)expr->expression)->identifier);
default: {
WARN("invalid delete, unimplemented exception message\n");
hres = compile_expression(ctx, expr->expression, TRUE);
if(FAILED(hres))
return hres;
return push_instr_uint_str(ctx, OP_throw_type, JS_E_INVALID_DELETE, L"FIXME");
}
}
return S_OK;
}
static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_t *expr, jsop_t op)
{
jsop_t assign_op = OP_throw_ref;
unsigned arg_cnt = 0;
HRESULT hres;
if(expr->expression1->type == EXPR_CALL) {
call_expression_t *call_expr = (call_expression_t*)expr->expression1;
argument_t *arg;
if(is_memberid_expr(call_expr->expression->type) && call_expr->argument_list) {
hres = compile_memberid_expression(ctx, call_expr->expression, fdexNameEnsure);
if(FAILED(hres))
return hres;
for(arg = call_expr->argument_list; arg; arg = arg->next) {
hres = compile_expression(ctx, arg->expr, TRUE);
if(FAILED(hres))
return hres;
arg_cnt++;
}
if(op != OP_LAST) {
unsigned instr;
/* We need to call the functions twice: to get the value and to set it.
* JavaScript interpreted functions may to modify value on the stack,
* but assignment calls are allowed only on external functions, so we
* may reuse the stack here. */
instr = push_instr(ctx, OP_call_member);
if(!instr)
return E_OUTOFMEMORY;
instr_ptr(ctx, instr)->u.arg[0].uint = arg_cnt;
instr_ptr(ctx, instr)->u.arg[1].lng = 1;
if(!push_instr(ctx, OP_push_acc))
return E_OUTOFMEMORY;
}
assign_op = OP_assign_call;
}
}else if(is_memberid_expr(expr->expression1->type)) {
if(op != OP_LAST || expr->expression1->type == EXPR_IDENT) {
hres = compile_memberid_expression(ctx, expr->expression1, fdexNameEnsure);
if(FAILED(hres))
return hres;
if(op != OP_LAST && !push_instr(ctx, OP_refval))
return E_OUTOFMEMORY;
assign_op = OP_assign;
}else {
hres = emit_member_expression(ctx, expr->expression1);
if(FAILED(hres))
return hres;
assign_op = OP_set_member;
}
}
if(assign_op == OP_throw_ref) {
/* Illegal assignment: evaluate and throw */
hres = compile_expression(ctx, expr->expression1, TRUE);
if(FAILED(hres))
return hres;
arg_cnt = JS_E_ILLEGAL_ASSIGN;
}
hres = compile_expression(ctx, expr->expression2, TRUE);
if(FAILED(hres))
return hres;
if(op != OP_LAST && !push_instr(ctx, op))
return E_OUTOFMEMORY;
return push_instr_uint(ctx, assign_op, arg_cnt);
}
static HRESULT compile_typeof_expression(compiler_ctx_t *ctx, unary_expression_t *expr)
{
jsop_t op;
HRESULT hres;
if(is_memberid_expr(expr->expression->type)) {
if(expr->expression->type == EXPR_IDENT)
return push_instr_bstr(ctx, OP_typeofident, ((identifier_expression_t*)expr->expression)->identifier);
op = OP_typeofid;
hres = compile_memberid_expression(ctx, expr->expression, 0);
}else {
op = OP_typeof;
hres = compile_expression(ctx, expr->expression, TRUE);
}
if(FAILED(hres))
return hres;
return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY;
}
static HRESULT compile_literal(compiler_ctx_t *ctx, literal_t *literal)
{
switch(literal->type) {
case LT_BOOL:
return push_instr_int(ctx, OP_bool, literal->u.bval);
case LT_DOUBLE:
return push_instr_double(ctx, OP_double, literal->u.dval);
case LT_NULL:
return push_instr(ctx, OP_null) ? S_OK : E_OUTOFMEMORY;
case LT_STRING:
return push_instr_str(ctx, OP_str, literal->u.str);
case LT_REGEXP:
return push_instr_str_uint(ctx, OP_regexp, literal->u.regexp.str, literal->u.regexp.flags);
DEFAULT_UNREACHABLE;
}
return E_FAIL;
}
static HRESULT literal_as_string(compiler_ctx_t *ctx, literal_t *literal, jsstr_t **str)
{
switch(literal->type) {
case LT_STRING:
*str = literal->u.str;
break;
case LT_DOUBLE:
return double_to_string(literal->u.dval, str);
DEFAULT_UNREACHABLE;
}
return *str ? S_OK : E_OUTOFMEMORY;
}
static HRESULT compile_array_literal(compiler_ctx_t *ctx, array_literal_expression_t *expr)
{
unsigned length = 0;
array_element_t *iter;
unsigned array_instr;
HRESULT hres;
array_instr = push_instr(ctx, OP_carray);
for(iter = expr->element_list; iter; iter = iter->next) {
length += iter->elision;
hres = compile_expression(ctx, iter->expr, TRUE);
if(FAILED(hres))
return hres;
hres = push_instr_uint(ctx, OP_carray_set, length);
if(FAILED(hres))
return hres;
length++;
}
instr_ptr(ctx, array_instr)->u.arg[0].uint = length + expr->length;
return S_OK;
}
static HRESULT compile_object_literal(compiler_ctx_t *ctx, property_value_expression_t *expr)
{
property_definition_t *iter;
jsstr_t *name;
HRESULT hres;
if(!push_instr(ctx, OP_new_obj))
return E_OUTOFMEMORY;
for(iter = expr->property_list; iter; iter = iter->next) {
hres = literal_as_string(ctx, iter->name, &name);
if(FAILED(hres))
return hres;
hres = compile_expression(ctx, iter->value, TRUE);
if(FAILED(hres))
return hres;
hres = push_instr_str_uint(ctx, OP_obj_prop, name, iter->type);
if(FAILED(hres))
return hres;
}
return S_OK;
}
static HRESULT compile_function_expression(compiler_ctx_t *ctx, function_expression_t *expr, BOOL emit_ret)
{
statement_ctx_t *stat_ctx;
assert(ctx->current_function_expr);
for(stat_ctx = ctx->stat_ctx; stat_ctx; stat_ctx = stat_ctx->next)
{
if(stat_ctx->block_scope)
break;
}
ctx->current_function_expr->scope_index = stat_ctx ? stat_ctx->scope_index : 0;
ctx->current_function_expr = ctx->current_function_expr->next;
return emit_ret ? push_instr_uint(ctx, OP_func, expr->func_id) : S_OK;
}
static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr, BOOL emit_ret)
{
HRESULT hres;
switch(expr->type) {
case EXPR_ADD:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_add);
break;
case EXPR_AND:
hres = compile_logical_expression(ctx, (binary_expression_t*)expr, OP_cnd_z);
break;
case EXPR_ARRAY:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_array);
break;
case EXPR_ARRAYLIT:
hres = compile_array_literal(ctx, (array_literal_expression_t*)expr);
break;
case EXPR_ASSIGN:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_LAST);
break;
case EXPR_ASSIGNADD:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_add);
break;
case EXPR_ASSIGNAND:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_and);
break;
case EXPR_ASSIGNSUB:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_sub);
break;
case EXPR_ASSIGNMUL:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_mul);
break;
case EXPR_ASSIGNDIV:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_div);
break;
case EXPR_ASSIGNMOD:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_mod);
break;
case EXPR_ASSIGNOR:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_or);
break;
case EXPR_ASSIGNLSHIFT:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_lshift);
break;
case EXPR_ASSIGNRSHIFT:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_rshift);
break;
case EXPR_ASSIGNRRSHIFT:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_rshift2);
break;
case EXPR_ASSIGNXOR:
hres = compile_assign_expression(ctx, (binary_expression_t*)expr, OP_xor);
break;
case EXPR_BAND:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_and);
break;
case EXPR_BITNEG:
hres = compile_unary_expression(ctx, (unary_expression_t*)expr, OP_bneg);
break;
case EXPR_BOR:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_or);
break;
case EXPR_CALL:
return compile_call_expression(ctx, (call_expression_t*)expr, emit_ret);
case EXPR_COMMA:
return compile_comma_expression(ctx, (binary_expression_t*)expr, emit_ret);
case EXPR_COND:
hres = compile_conditional_expression(ctx, (conditional_expression_t*)expr);
break;
case EXPR_DELETE:
hres = compile_delete_expression(ctx, (unary_expression_t*)expr);
break;
case EXPR_DIV:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_div);
break;
case EXPR_EQ:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_eq);
break;
case EXPR_EQEQ:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_eq2);
break;
case EXPR_FUNC:
return compile_function_expression(ctx, (function_expression_t*)expr, emit_ret);
case EXPR_GREATER:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_gt);
break;
case EXPR_GREATEREQ:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_gteq);
break;
case EXPR_IDENT:
hres = emit_identifier(ctx, ((identifier_expression_t*)expr)->identifier);
break;
case EXPR_IN:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_in);
break;
case EXPR_INSTANCEOF:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_instanceof);
break;
case EXPR_LESS:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_lt);
break;
case EXPR_LESSEQ:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_lteq);
break;
case EXPR_LITERAL:
hres = compile_literal(ctx, ((literal_expression_t*)expr)->literal);
break;
case EXPR_LOGNEG:
hres = compile_unary_expression(ctx, (unary_expression_t*)expr, OP_neg);
break;
case EXPR_LSHIFT:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_lshift);
break;
case EXPR_MEMBER:
hres = compile_member_expression(ctx, (member_expression_t*)expr);
break;
case EXPR_MINUS:
hres = compile_unary_expression(ctx, (unary_expression_t*)expr, OP_minus);
break;
case EXPR_MOD:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_mod);
break;
case EXPR_MUL:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_mul);
break;
case EXPR_NEW:
hres = compile_new_expression(ctx, (call_expression_t*)expr);
break;
case EXPR_NOTEQ:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_neq);
break;
case EXPR_NOTEQEQ:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_neq2);
break;
case EXPR_OR:
hres = compile_logical_expression(ctx, (binary_expression_t*)expr, OP_cnd_nz);
break;
case EXPR_PLUS:
hres = compile_unary_expression(ctx, (unary_expression_t*)expr, OP_tonum);
break;
case EXPR_POSTDEC:
hres = compile_increment_expression(ctx, (unary_expression_t*)expr, OP_postinc, -1);
break;
case EXPR_POSTINC:
hres = compile_increment_expression(ctx, (unary_expression_t*)expr, OP_postinc, 1);
break;
case EXPR_PREDEC:
hres = compile_increment_expression(ctx, (unary_expression_t*)expr, OP_preinc, -1);
break;
case EXPR_PREINC:
hres = compile_increment_expression(ctx, (unary_expression_t*)expr, OP_preinc, 1);
break;
case EXPR_PROPVAL:
hres = compile_object_literal(ctx, (property_value_expression_t*)expr);
break;
case EXPR_RSHIFT:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_rshift);
break;
case EXPR_RRSHIFT:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_rshift2);
break;
case EXPR_SUB:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_sub);
break;
case EXPR_THIS:
return !emit_ret || push_instr(ctx, OP_this) ? S_OK : E_OUTOFMEMORY;
case EXPR_TYPEOF:
hres = compile_typeof_expression(ctx, (unary_expression_t*)expr);
break;
case EXPR_VOID:
hres = compile_unary_expression(ctx, (unary_expression_t*)expr, OP_void);
break;
case EXPR_BXOR:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_xor);
break;
DEFAULT_UNREACHABLE;
}
if(FAILED(hres))
return hres;
return emit_ret ? S_OK : push_instr_uint(ctx, OP_pop, 1);
}
static inline BOOL is_loop_statement(statement_type_t type)
{
return type == STAT_FOR || type == STAT_FORIN || type == STAT_WHILE;
}
/* ECMA-262 3rd Edition 12.1 */
static HRESULT compile_block_statement(compiler_ctx_t *ctx, block_statement_t *block, statement_t *iter)
{
statement_ctx_t stat_ctx = {0, TRUE};
BOOL needs_scope;
HRESULT hres;
needs_scope = block && block->scope_index;
if (needs_scope)
{
if(FAILED(hres = push_instr_uint(ctx, OP_push_block_scope, block->scope_index)))
return hres;
stat_ctx.scope_index = block->scope_index;
stat_ctx.block_scope = TRUE;
}
while(iter) {
hres = compile_statement(ctx, needs_scope ? &stat_ctx : NULL, iter);
if(FAILED(hres))
return hres;
iter = iter->next;
}
if(needs_scope && !push_instr(ctx, OP_pop_scope))
return E_OUTOFMEMORY;
return S_OK;
}
/* ECMA-262 3rd Edition 12.2 */
static HRESULT compile_variable_list(compiler_ctx_t *ctx, variable_declaration_t *list)
{
variable_declaration_t *iter;
HRESULT hres;
assert(list != NULL);
for(iter = list; iter; iter = iter->next) {
if(!iter->expr)
continue;
if (iter->constant)
FIXME("Constant variables are not supported.\n");
hres = emit_identifier_ref(ctx, iter->identifier, 0);
if(FAILED(hres))
return hres;
hres = compile_expression(ctx, iter->expr, TRUE);
if(FAILED(hres))
return hres;
if(!push_instr(ctx, OP_assign))
return E_OUTOFMEMORY;
hres = push_instr_uint(ctx, OP_pop, 1);
if(FAILED(hres))
return hres;
}
return S_OK;
}
/* ECMA-262 3rd Edition 12.2 */
static HRESULT compile_var_statement(compiler_ctx_t *ctx, var_statement_t *stat)
{
return compile_variable_list(ctx, stat->variable_list);
}
/* ECMA-262 3rd Edition 12.4 */
static HRESULT compile_expression_statement(compiler_ctx_t *ctx, expression_statement_t *stat)
{
HRESULT hres;
hres = compile_expression(ctx, stat->expr, ctx->from_eval);
if(FAILED(hres))
return hres;
return !ctx->from_eval || push_instr(ctx, OP_setret) ? S_OK : E_OUTOFMEMORY;
}
/* ECMA-262 3rd Edition 12.5 */
static HRESULT compile_if_statement(compiler_ctx_t *ctx, if_statement_t *stat)
{
unsigned jmp_else;
HRESULT hres;
hres = compile_expression(ctx, stat->expr, TRUE);
if(FAILED(hres))
return hres;
jmp_else = push_instr(ctx, OP_jmp_z);
if(!jmp_else)
return E_OUTOFMEMORY;
hres = compile_statement(ctx, NULL, stat->if_stat);
if(FAILED(hres))
return hres;
if(stat->else_stat) {
unsigned jmp_end;
jmp_end = push_instr(ctx, OP_jmp);
if(!jmp_end)
return E_OUTOFMEMORY;
set_arg_uint(ctx, jmp_else, ctx->code_off);
hres = compile_statement(ctx, NULL, stat->else_stat);
if(FAILED(hres))
return hres;
set_arg_uint(ctx, jmp_end, ctx->code_off);
}else {
set_arg_uint(ctx, jmp_else, ctx->code_off);
}
return S_OK;
}
/* ECMA-262 3rd Edition 12.6.2 */
static HRESULT compile_while_statement(compiler_ctx_t *ctx, while_statement_t *stat)
{
statement_ctx_t stat_ctx = {0, FALSE, FALSE};
unsigned jmp_off;
HRESULT hres;
stat_ctx.break_label = alloc_label(ctx);
if(!stat_ctx.break_label)
return E_OUTOFMEMORY;
stat_ctx.continue_label = alloc_label(ctx);
if(!stat_ctx.continue_label)
return E_OUTOFMEMORY;
jmp_off = ctx->code_off;
if(!stat->do_while) {
label_set_addr(ctx, stat_ctx.continue_label);
hres = compile_expression(ctx, stat->expr, TRUE);
if(FAILED(hres))
return hres;
hres = push_instr_uint(ctx, OP_jmp_z, stat_ctx.break_label);
if(FAILED(hres))
return hres;
}
hres = compile_statement(ctx, &stat_ctx, stat->statement);
if(FAILED(hres))
return hres;
set_compiler_loc(ctx, stat->stat.loc);
if(stat->do_while) {
label_set_addr(ctx, stat_ctx.continue_label);
hres = compile_expression(ctx, stat->expr, TRUE);
if(FAILED(hres))
return hres;
hres = push_instr_uint(ctx, OP_jmp_z, stat_ctx.break_label);
if(FAILED(hres))
return hres;
}
hres = push_instr_uint(ctx, OP_jmp, jmp_off);
if(FAILED(hres))
return hres;
label_set_addr(ctx, stat_ctx.break_label);
return S_OK;
}
/* ECMA-262 10th Edition 13.7.4 */
static HRESULT compile_for_statement(compiler_ctx_t *ctx, for_statement_t *stat)
{
statement_ctx_t stat_ctx = {0, FALSE, FALSE};
statement_ctx_t scope_stat_ctx = {0, TRUE};
unsigned expr_off;
HRESULT hres;
if (stat->scope_index)
{
if(FAILED(hres = push_instr_uint(ctx, OP_push_block_scope, stat->scope_index)))
return hres;
scope_stat_ctx.scope_index = stat->scope_index;
scope_stat_ctx.block_scope = TRUE;
push_compiler_statement_ctx(ctx, &scope_stat_ctx);
}
if(stat->variable_list) {
hres = compile_variable_list(ctx, stat->variable_list);
if(FAILED(hres))
goto done;
}else if(stat->begin_expr) {
hres = compile_expression(ctx, stat->begin_expr, FALSE);
if(FAILED(hres))
goto done;
}
stat_ctx.break_label = alloc_label(ctx);
if(!stat_ctx.break_label)
{
hres = E_OUTOFMEMORY;
goto done;
}
stat_ctx.continue_label = alloc_label(ctx);
if(!stat_ctx.continue_label)
{
hres = E_OUTOFMEMORY;
goto done;
}
expr_off = ctx->code_off;
if(stat->expr) {
set_compiler_loc(ctx, stat->expr_loc);
hres = compile_expression(ctx, stat->expr, TRUE);
if(FAILED(hres))
goto done;
hres = push_instr_uint(ctx, OP_jmp_z, stat_ctx.break_label);
if(FAILED(hres))
goto done;
}
hres = compile_statement(ctx, &stat_ctx, stat->statement);
if(FAILED(hres))
goto done;
label_set_addr(ctx, stat_ctx.continue_label);
if(stat->end_expr) {
set_compiler_loc(ctx, stat->end_loc);
hres = compile_expression(ctx, stat->end_expr, FALSE);
if(FAILED(hres))
goto done;
}
hres = push_instr_uint(ctx, OP_jmp, expr_off);
if(FAILED(hres))
goto done;
label_set_addr(ctx, stat_ctx.break_label);
hres = S_OK;
done:
if (stat->scope_index)
{
pop_compiler_statement_ctx(ctx, &scope_stat_ctx);
if(SUCCEEDED(hres) && !push_instr(ctx, OP_pop_scope))
return E_OUTOFMEMORY;
}
return hres;
}
/* ECMA-262 3rd Edition 12.6.4 */
static HRESULT compile_forin_statement(compiler_ctx_t *ctx, forin_statement_t *stat)
{
statement_ctx_t stat_ctx = {4, FALSE, FALSE};
HRESULT hres;
if(stat->variable) {
hres = compile_variable_list(ctx, stat->variable);
if(FAILED(hres))
return hres;
}
stat_ctx.break_label = alloc_label(ctx);
if(!stat_ctx.break_label)
return E_OUTOFMEMORY;
stat_ctx.continue_label = alloc_label(ctx);
if(!stat_ctx.continue_label)
return E_OUTOFMEMORY;
hres = compile_expression(ctx, stat->in_expr, TRUE);
if(FAILED(hres))
return hres;
if(stat->variable) {
hres = emit_identifier_ref(ctx, stat->variable->identifier, fdexNameEnsure);
if(FAILED(hres))
return hres;
}else if(is_memberid_expr(stat->expr->type)) {
hres = compile_memberid_expression(ctx, stat->expr, fdexNameEnsure);
if(FAILED(hres))
return hres;
}else {
hres = push_instr_uint(ctx, OP_throw_ref, JS_E_ILLEGAL_ASSIGN);
if(FAILED(hres))
return hres;
/* FIXME: compile statement anyways when we depend on compiler to check errors */
return S_OK;
}
hres = push_instr_int(ctx, OP_int, DISPID_STARTENUM);
if(FAILED(hres))
return hres;
label_set_addr(ctx, stat_ctx.continue_label);
hres = push_instr_uint(ctx, OP_forin, stat_ctx.break_label);
if(FAILED(hres))
return E_OUTOFMEMORY;
hres = compile_statement(ctx, &stat_ctx, stat->statement);
if(FAILED(hres))
return hres;
hres = push_instr_uint(ctx, OP_jmp, stat_ctx.continue_label);
if(FAILED(hres))
return hres;
label_set_addr(ctx, stat_ctx.break_label);
return S_OK;
}
static HRESULT pop_to_stat(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx)
{
unsigned stack_pop = 0;
statement_ctx_t *iter;
HRESULT hres;
for(iter = ctx->stat_ctx; iter != stat_ctx; iter = iter->next) {
if(iter->using_scope && !push_instr(ctx, OP_pop_scope))
return E_OUTOFMEMORY;
if(iter->using_except) {
if(stack_pop) {
hres = push_instr_uint(ctx, OP_pop, stack_pop);
if(FAILED(hres))
return hres;
stack_pop = 0;
}
hres = push_instr_uint(ctx, OP_pop_except, ctx->code_off+1);
if(FAILED(hres))
return hres;
}
stack_pop += iter->stack_use;
}
if(stack_pop) {
hres = push_instr_uint(ctx, OP_pop, stack_pop);
if(FAILED(hres))
return hres;
}
return S_OK;
}
/* ECMA-262 3rd Edition 12.7 */
static HRESULT compile_continue_statement(compiler_ctx_t *ctx, branch_statement_t *stat)
{
statement_ctx_t *pop_ctx;
HRESULT hres;
if(stat->identifier) {
statement_t *label_stat;
statement_ctx_t *iter;
pop_ctx = NULL;
for(iter = ctx->stat_ctx; iter; iter = iter->next) {
if(iter->continue_label)
pop_ctx = iter;
if(iter->labelled_stat && !wcscmp(iter->labelled_stat->identifier, stat->identifier))
break;
}
if(!iter) {
WARN("Label not found\n");
return JS_E_LABEL_NOT_FOUND;
}
/* Labelled continue are allowed only on loops */
for(label_stat = iter->labelled_stat->statement;
label_stat->type == STAT_LABEL;
label_stat = ((labelled_statement_t*)label_stat)->statement);
if(!is_loop_statement(label_stat->type)) {
WARN("Label is not a loop\n");
return JS_E_INVALID_CONTINUE;
}
assert(pop_ctx != NULL);
}else {
for(pop_ctx = ctx->stat_ctx; pop_ctx; pop_ctx = pop_ctx->next) {
if(pop_ctx->continue_label)
break;
}
if(!pop_ctx) {
WARN("continue outside loop\n");
return JS_E_INVALID_CONTINUE;
}
}
hres = pop_to_stat(ctx, pop_ctx);
if(FAILED(hres))
return hres;
return push_instr_uint(ctx, OP_jmp, pop_ctx->continue_label);
}
/* ECMA-262 3rd Edition 12.8 */
static HRESULT compile_break_statement(compiler_ctx_t *ctx, branch_statement_t *stat)
{
statement_ctx_t *pop_ctx;
HRESULT hres;
if(stat->identifier) {
for(pop_ctx = ctx->stat_ctx; pop_ctx; pop_ctx = pop_ctx->next) {
if(pop_ctx->labelled_stat && !wcscmp(pop_ctx->labelled_stat->identifier, stat->identifier)) {
assert(pop_ctx->break_label);
break;
}
}
if(!pop_ctx) {
WARN("Label not found\n");
return JS_E_LABEL_NOT_FOUND;
}
}else {
for(pop_ctx = ctx->stat_ctx; pop_ctx; pop_ctx = pop_ctx->next) {
if(pop_ctx->break_label && !pop_ctx->labelled_stat)
break;
}
if(!pop_ctx) {
WARN("Break outside loop\n");
return JS_E_INVALID_BREAK;
}
}
hres = pop_to_stat(ctx, pop_ctx->next);
if(FAILED(hres))
return hres;
return push_instr_uint(ctx, OP_jmp, pop_ctx->break_label);
}
/* ECMA-262 3rd Edition 12.9 */
static HRESULT compile_return_statement(compiler_ctx_t *ctx, expression_statement_t *stat)
{
HRESULT hres;
if(ctx->from_eval) {
WARN("misplaced return statement\n");
return JS_E_MISPLACED_RETURN;
}
if(stat->expr) {
hres = compile_expression(ctx, stat->expr, TRUE);
if(FAILED(hres))
return hres;
if(!push_instr(ctx, OP_setret))
return E_OUTOFMEMORY;
}
hres = pop_to_stat(ctx, NULL);
if(FAILED(hres))
return hres;
return push_instr_uint(ctx, OP_ret, !stat->expr);
}
/* ECMA-262 3rd Edition 12.10 */
static HRESULT compile_with_statement(compiler_ctx_t *ctx, with_statement_t *stat)
{
statement_ctx_t stat_ctx = {0, TRUE, FALSE};
HRESULT hres;
hres = compile_expression(ctx, stat->expr, TRUE);
if(FAILED(hres))
return hres;
if(!push_instr(ctx, OP_push_with_scope))
return E_OUTOFMEMORY;
hres = compile_statement(ctx, &stat_ctx, stat->statement);
if(FAILED(hres))
return hres;
if(!push_instr(ctx, OP_pop_scope))
return E_OUTOFMEMORY;
return S_OK;
}
/* ECMA-262 3rd Edition 12.10 */
static HRESULT compile_labelled_statement(compiler_ctx_t *ctx, labelled_statement_t *stat)
{
statement_ctx_t stat_ctx = {0, FALSE, FALSE, 0, 0, stat}, *iter;
HRESULT hres;
for(iter = ctx->stat_ctx; iter; iter = iter->next) {
if(iter->labelled_stat && !wcscmp(iter->labelled_stat->identifier, stat->identifier)) {
WARN("Label %s redefined\n", debugstr_w(stat->identifier));
return JS_E_LABEL_REDEFINED;
}
}
/* Labelled breaks are allowed for any labelled statements, not only loops (violating spec) */
stat_ctx.break_label = alloc_label(ctx);
if(!stat_ctx.break_label)
return E_OUTOFMEMORY;
hres = compile_statement(ctx, &stat_ctx, stat->statement);
if(FAILED(hres))
return hres;
label_set_addr(ctx, stat_ctx.break_label);
return S_OK;
}
/* ECMA-262 3rd Edition 12.13 */
static HRESULT compile_switch_statement(compiler_ctx_t *ctx, switch_statement_t *stat)
{
statement_ctx_t stat_ctx = {0, FALSE, FALSE};
unsigned case_cnt = 0, *case_jmps, i, default_jmp;
BOOL have_default = FALSE;
statement_t *stat_iter;
case_clausule_t *iter;
HRESULT hres;
hres = compile_expression(ctx, stat->expr, TRUE);
if(FAILED(hres))
return hres;
stat_ctx.break_label = alloc_label(ctx);
if(!stat_ctx.break_label)
return E_OUTOFMEMORY;
for(iter = stat->case_list; iter; iter = iter->next) {
if(iter->expr)
case_cnt++;
}
case_jmps = malloc(case_cnt * sizeof(*case_jmps));
if(!case_jmps)
return E_OUTOFMEMORY;
i = 0;
for(iter = stat->case_list; iter; iter = iter->next) {
if(!iter->expr) {
have_default = TRUE;
continue;
}
set_compiler_loc(ctx, iter->loc);
hres = compile_expression(ctx, iter->expr, TRUE);
if(FAILED(hres))
break;
case_jmps[i] = push_instr(ctx, OP_case);
if(!case_jmps[i]) {
hres = E_OUTOFMEMORY;
break;
}
i++;
}
if(SUCCEEDED(hres)) {
hres = push_instr_uint(ctx, OP_pop, 1);
if(SUCCEEDED(hres)) {
default_jmp = push_instr(ctx, OP_jmp);
if(!default_jmp)
hres = E_OUTOFMEMORY;
}
}
if(FAILED(hres)) {
free(case_jmps);
return hres;
}
i = 0;
for(iter = stat->case_list; iter; iter = iter->next) {
while(iter->next && iter->next->stat == iter->stat) {
set_arg_uint(ctx, iter->expr ? case_jmps[i++] : default_jmp, ctx->code_off);
iter = iter->next;
}
set_arg_uint(ctx, iter->expr ? case_jmps[i++] : default_jmp, ctx->code_off);
for(stat_iter = iter->stat; stat_iter && (!iter->next || iter->next->stat != stat_iter);
stat_iter = stat_iter->next) {
hres = compile_statement(ctx, &stat_ctx, stat_iter);
if(FAILED(hres))
break;
}
if(FAILED(hres))
break;
}
free(case_jmps);
if(FAILED(hres))
return hres;
assert(i == case_cnt);
if(!have_default) {
hres = push_instr_uint(ctx, OP_jmp, stat_ctx.break_label);
if(FAILED(hres))
return hres;
set_arg_uint(ctx, default_jmp, ctx->code_off);
}
label_set_addr(ctx, stat_ctx.break_label);
return S_OK;
}
/* ECMA-262 3rd Edition 12.13 */
static HRESULT compile_throw_statement(compiler_ctx_t *ctx, expression_statement_t *stat)
{
HRESULT hres;
hres = compile_expression(ctx, stat->expr, TRUE);
if(FAILED(hres))
return hres;
return push_instr(ctx, OP_throw) ? S_OK : E_OUTOFMEMORY;
}
/* ECMA-262 3rd Edition 12.14 */
static HRESULT compile_try_statement(compiler_ctx_t *ctx, try_statement_t *stat)
{
statement_ctx_t try_ctx = {0, FALSE, TRUE}, finally_ctx = {2, FALSE, FALSE};
unsigned push_except, finally_off = 0, catch_off = 0, pop_except, catch_pop_except = 0;
BSTR ident;
HRESULT hres;
push_except = push_instr(ctx, OP_push_except);
if(!push_except)
return E_OUTOFMEMORY;
if(stat->catch_block) {
ident = compiler_alloc_bstr(ctx, stat->catch_block->identifier);
if(!ident)
return E_OUTOFMEMORY;
}else {
ident = NULL;
}
hres = compile_statement(ctx, &try_ctx, stat->try_statement);
if(FAILED(hres))
return hres;
pop_except = push_instr(ctx, OP_pop_except);
if(!pop_except)
return E_OUTOFMEMORY;
if(stat->catch_block) {
statement_ctx_t catch_ctx = {0, TRUE, stat->finally_statement != NULL};
if(stat->finally_statement)
catch_ctx.using_except = TRUE;
catch_off = ctx->code_off;
hres = push_instr_bstr(ctx, OP_enter_catch, ident);
if(FAILED(hres))
return hres;
hres = compile_statement(ctx, &catch_ctx, stat->catch_block->statement);
if(FAILED(hres))
return hres;
if(!push_instr(ctx, OP_pop_scope))
return E_OUTOFMEMORY;
if(stat->finally_statement) {
catch_pop_except = push_instr(ctx, OP_pop_except);
if(!catch_pop_except)
return E_OUTOFMEMORY;
}
}
if(stat->finally_statement) {
/*
* finally block expects two elements on the stack, which may be:
* - (true, return_addr) set by OP_pop_except, OP_end_finally jumps back to passed address
* - (false, exception_value) set when unwinding an exception, which OP_end_finally rethrows
*/
finally_off = ctx->code_off;
hres = compile_statement(ctx, &finally_ctx, stat->finally_statement);
if(FAILED(hres))
return hres;
set_compiler_loc(ctx, stat->finally_loc);
if(!push_instr(ctx, OP_end_finally))
return E_OUTOFMEMORY;
}
instr_ptr(ctx, pop_except)->u.arg[0].uint = ctx->code_off;
if(catch_pop_except)
instr_ptr(ctx, catch_pop_except)->u.arg[0].uint = ctx->code_off;
instr_ptr(ctx, push_except)->u.arg[0].uint = catch_off;
instr_ptr(ctx, push_except)->u.arg[1].uint = finally_off;
return S_OK;
}
static HRESULT compile_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx, statement_t *stat)
{
HRESULT hres;
push_compiler_statement_ctx(ctx, stat_ctx);
set_compiler_loc(ctx, stat->loc);
switch(stat->type) {
case STAT_BLOCK:
hres = compile_block_statement(ctx, (block_statement_t*)stat, ((block_statement_t*)stat)->stat_list);
break;
case STAT_BREAK:
hres = compile_break_statement(ctx, (branch_statement_t*)stat);
break;
case STAT_CONTINUE:
hres = compile_continue_statement(ctx, (branch_statement_t*)stat);
break;
case STAT_EMPTY:
/* nothing to do */
hres = S_OK;
break;
case STAT_EXPR:
hres = compile_expression_statement(ctx, (expression_statement_t*)stat);
break;
case STAT_FOR:
hres = compile_for_statement(ctx, (for_statement_t*)stat);
break;
case STAT_FORIN:
hres = compile_forin_statement(ctx, (forin_statement_t*)stat);
break;
case STAT_IF:
hres = compile_if_statement(ctx, (if_statement_t*)stat);
break;
case STAT_LABEL:
hres = compile_labelled_statement(ctx, (labelled_statement_t*)stat);
break;
case STAT_RETURN:
hres = compile_return_statement(ctx, (expression_statement_t*)stat);
break;
case STAT_SWITCH:
hres = compile_switch_statement(ctx, (switch_statement_t*)stat);
break;
case STAT_THROW:
hres = compile_throw_statement(ctx, (expression_statement_t*)stat);
break;
case STAT_TRY:
hres = compile_try_statement(ctx, (try_statement_t*)stat);
break;
case STAT_VAR:
hres = compile_var_statement(ctx, (var_statement_t*)stat);
break;
case STAT_WHILE:
hres = compile_while_statement(ctx, (while_statement_t*)stat);
break;
case STAT_WITH:
hres = compile_with_statement(ctx, (with_statement_t*)stat);
break;
DEFAULT_UNREACHABLE;
}
pop_compiler_statement_ctx(ctx, stat_ctx);
return hres;
}
static inline function_local_t *find_local(compiler_ctx_t *ctx, const WCHAR *name, unsigned int scope)
{
struct wine_rb_entry *entry = wine_rb_get(&ctx->local_scopes[scope].locals, name);
return entry ? WINE_RB_ENTRY_VALUE(entry, function_local_t, entry) : NULL;
}
static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref, unsigned int scope)
{
function_local_t *local;
local = heap_pool_alloc(&ctx->heap, sizeof(*local));
if(!local)
return FALSE;
local->name = name;
local->ref = ref;
wine_rb_put(&ctx->local_scopes[scope].locals, name, &local->entry);
ctx->local_scopes[scope].locals_cnt++;
return TRUE;
}
static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name, unsigned int scope)
{
BSTR ident;
if(find_local(ctx, name, scope))
return TRUE;
ident = compiler_alloc_bstr(ctx, name);
if(!ident)
return FALSE;
return alloc_local(ctx, ident, ctx->func->var_cnt++, scope);
}
static HRESULT visit_function_expression(compiler_ctx_t *ctx, function_expression_t *expr)
{
statement_ctx_t *stat_ctx;
expr->func_id = ctx->func->func_cnt++;
ctx->func_tail = ctx->func_tail ? (ctx->func_tail->next = expr) : (ctx->func_head = expr);
if(!expr->identifier || expr->event_target)
return S_OK;
for (stat_ctx = ctx->stat_ctx; stat_ctx; stat_ctx = stat_ctx->next)
{
if (stat_ctx->block_scope)
{
stat_ctx->scope_has_functions = TRUE;
break;
}
}
if(!expr->is_statement && ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5)
return S_OK;
return alloc_variable(ctx, expr->identifier, stat_ctx ? stat_ctx->scope_index : 0) ? S_OK : E_OUTOFMEMORY;
}
static HRESULT visit_expression(compiler_ctx_t *ctx, expression_t *expr)
{
HRESULT hres = S_OK;
switch(expr->type) {
case EXPR_ADD:
case EXPR_AND:
case EXPR_ARRAY:
case EXPR_ASSIGN:
case EXPR_ASSIGNADD:
case EXPR_ASSIGNAND:
case EXPR_ASSIGNSUB:
case EXPR_ASSIGNMUL:
case EXPR_ASSIGNDIV:
case EXPR_ASSIGNMOD:
case EXPR_ASSIGNOR:
case EXPR_ASSIGNLSHIFT:
case EXPR_ASSIGNRSHIFT:
case EXPR_ASSIGNRRSHIFT:
case EXPR_ASSIGNXOR:
case EXPR_BAND:
case EXPR_BOR:
case EXPR_COMMA:
case EXPR_DIV:
case EXPR_EQ:
case EXPR_EQEQ:
case EXPR_GREATER:
case EXPR_GREATEREQ:
case EXPR_IN:
case EXPR_INSTANCEOF:
case EXPR_LESS:
case EXPR_LESSEQ:
case EXPR_LSHIFT:
case EXPR_MOD:
case EXPR_MUL:
case EXPR_NOTEQ:
case EXPR_NOTEQEQ:
case EXPR_OR:
case EXPR_RSHIFT:
case EXPR_RRSHIFT:
case EXPR_SUB:
case EXPR_BXOR: {
binary_expression_t *binary_expr = (binary_expression_t*)expr;
hres = visit_expression(ctx, binary_expr->expression1);
if(FAILED(hres))
return hres;
hres = visit_expression(ctx, binary_expr->expression2);
break;
}
case EXPR_BITNEG:
case EXPR_DELETE:
case EXPR_LOGNEG:
case EXPR_MINUS:
case EXPR_PLUS:
case EXPR_POSTDEC:
case EXPR_POSTINC:
case EXPR_PREDEC:
case EXPR_PREINC:
case EXPR_TYPEOF:
case EXPR_VOID:
hres = visit_expression(ctx, ((unary_expression_t*)expr)->expression);
break;
case EXPR_IDENT:
case EXPR_LITERAL:
case EXPR_THIS:
break;
case EXPR_ARRAYLIT: {
array_literal_expression_t *array_expr = (array_literal_expression_t*)expr;
array_element_t *iter;
for(iter = array_expr->element_list; iter; iter = iter->next) {
hres = visit_expression(ctx, iter->expr);
if(FAILED(hres))
return hres;
}
break;
}
case EXPR_CALL:
case EXPR_NEW: {
call_expression_t *call_expr = (call_expression_t*)expr;
argument_t *arg;
hres = visit_expression(ctx, call_expr->expression);
if(FAILED(hres))
return hres;
for(arg = call_expr->argument_list; arg; arg = arg->next) {
hres = visit_expression(ctx, arg->expr);
if(FAILED(hres))
return hres;
}
break;
}
case EXPR_COND: {
conditional_expression_t *cond_expr = (conditional_expression_t*)expr;
hres = visit_expression(ctx, cond_expr->expression);
if(FAILED(hres))
return hres;
hres = visit_expression(ctx, cond_expr->true_expression);
if(FAILED(hres))
return hres;
hres = visit_expression(ctx, cond_expr->false_expression);
break;
}
case EXPR_FUNC:
hres = visit_function_expression(ctx, (function_expression_t*)expr);
break;
case EXPR_MEMBER:
hres = visit_expression(ctx, ((member_expression_t*)expr)->expression);
break;
case EXPR_PROPVAL: {
property_definition_t *iter;
for(iter = ((property_value_expression_t*)expr)->property_list; iter; iter = iter->next) {
hres = visit_expression(ctx, iter->value);
if(FAILED(hres))
return hres;
}
break;
}
DEFAULT_UNREACHABLE;
}
return hres;
}
static HRESULT visit_variable_list(compiler_ctx_t *ctx, variable_declaration_t *list)
{
variable_declaration_t *iter;
statement_ctx_t *stat_ctx;
HRESULT hres;
for(iter = list; iter; iter = iter->next) {
for (stat_ctx = ctx->stat_ctx; stat_ctx; stat_ctx = stat_ctx->next)
{
if (stat_ctx->block_scope)
break;
}
if(!alloc_variable(ctx, iter->identifier, iter->block_scope && stat_ctx ? stat_ctx->scope_index : 0))
return E_OUTOFMEMORY;
if(iter->expr) {
hres = visit_expression(ctx, iter->expr);
if(FAILED(hres))
return hres;
}
}
return S_OK;
}
static HRESULT visit_statement(compiler_ctx_t*,statement_ctx_t *,statement_t*);
static HRESULT visit_block_statement(compiler_ctx_t *ctx, block_statement_t *block, statement_t *iter)
{
statement_ctx_t stat_ctx = {0, TRUE};
BOOL needs_scope;
HRESULT hres;
needs_scope = block && ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5;
if (needs_scope)
{
if (!alloc_local_scope(ctx, &block->scope_index))
return E_OUTOFMEMORY;
stat_ctx.scope_index = block->scope_index;
stat_ctx.block_scope = TRUE;
}
while(iter) {
hres = visit_statement(ctx, needs_scope ? &stat_ctx : NULL, iter);
if(FAILED(hres))
return hres;
iter = iter->next;
}
if (needs_scope && !(ctx->local_scopes[stat_ctx.scope_index].locals_cnt || stat_ctx.scope_has_functions))
remove_local_scope(ctx, block->scope_index);
return S_OK;
}
static HRESULT visit_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx, statement_t *stat)
{
HRESULT hres = S_OK;
push_compiler_statement_ctx(ctx, stat_ctx);
switch(stat->type) {
case STAT_BLOCK:
hres = visit_block_statement(ctx, (block_statement_t*)stat, ((block_statement_t*)stat)->stat_list);
break;
case STAT_BREAK:
case STAT_CONTINUE:
case STAT_EMPTY:
break;
case STAT_EXPR: {
expression_statement_t *expr_stat = (expression_statement_t*)stat;
if(expr_stat->expr) {
if(expr_stat->expr->type == EXPR_FUNC)
((function_expression_t*)expr_stat->expr)->is_statement = TRUE;
hres = visit_expression(ctx, expr_stat->expr);
}
break;
}
case STAT_RETURN:
case STAT_THROW: {
expression_statement_t *expr_stat = (expression_statement_t*)stat;
if(expr_stat->expr)
hres = visit_expression(ctx, expr_stat->expr);
break;
}
case STAT_FOR: {
statement_ctx_t stat_ctx_data = {0, TRUE}, *stat_ctx = NULL;
for_statement_t *for_stat = (for_statement_t*)stat;
if(for_stat->variable_list)
{
variable_declaration_t *var;
for(var = for_stat->variable_list; var; var = var->next)
{
if (var->block_scope)
{
stat_ctx = &stat_ctx_data;
break;
}
}
if (stat_ctx)
{
if (!alloc_local_scope(ctx, &for_stat->scope_index))
{
hres = E_OUTOFMEMORY;
break;
}
stat_ctx->scope_index = for_stat->scope_index;
stat_ctx->block_scope = TRUE;
push_compiler_statement_ctx(ctx, stat_ctx);
}
hres = visit_variable_list(ctx, for_stat->variable_list);
}
else if(for_stat->begin_expr)
hres = visit_expression(ctx, for_stat->begin_expr);
if(FAILED(hres))
{
pop_compiler_statement_ctx(ctx, stat_ctx);
break;
}
if(for_stat->expr) {
hres = visit_expression(ctx, for_stat->expr);
if(FAILED(hres))
{
pop_compiler_statement_ctx(ctx, stat_ctx);
break;
}
}
hres = visit_statement(ctx, NULL, for_stat->statement);
if(FAILED(hres))
{
pop_compiler_statement_ctx(ctx, stat_ctx);
break;
}
if(for_stat->end_expr)
hres = visit_expression(ctx, for_stat->end_expr);
pop_compiler_statement_ctx(ctx, stat_ctx);
break;
}
case STAT_FORIN: {
forin_statement_t *forin_stat = (forin_statement_t*)stat;
if(forin_stat->variable) {
hres = visit_variable_list(ctx, forin_stat->variable);
if(FAILED(hres))
break;
}
hres = visit_expression(ctx, forin_stat->in_expr);
if(FAILED(hres))
return hres;
if(forin_stat->expr) {
hres = visit_expression(ctx, forin_stat->expr);
if(FAILED(hres))
return hres;
}
hres = visit_statement(ctx, NULL, forin_stat->statement);
break;
}
case STAT_IF: {
if_statement_t *if_stat = (if_statement_t*)stat;
hres = visit_expression(ctx, if_stat->expr);
if(FAILED(hres))
return hres;
hres = visit_statement(ctx, NULL, if_stat->if_stat);
if(FAILED(hres))
return hres;
if(if_stat->else_stat)
hres = visit_statement(ctx, NULL, if_stat->else_stat);
break;
}
case STAT_LABEL:
hres = visit_statement(ctx, NULL, ((labelled_statement_t*)stat)->statement);
break;
case STAT_SWITCH: {
switch_statement_t *switch_stat = (switch_statement_t*)stat;
statement_t *stat_iter;
case_clausule_t *iter;
hres = visit_expression(ctx, switch_stat->expr);
if(FAILED(hres))
return hres;
for(iter = switch_stat->case_list; iter; iter = iter->next) {
if(!iter->expr)
continue;
hres = visit_expression(ctx, iter->expr);
if(FAILED(hres))
return hres;
}
for(iter = switch_stat->case_list; iter; iter = iter->next) {
while(iter->next && iter->next->stat == iter->stat)
iter = iter->next;
for(stat_iter = iter->stat; stat_iter && (!iter->next || iter->next->stat != stat_iter);
stat_iter = stat_iter->next) {
hres = visit_statement(ctx, NULL, stat_iter);
if(FAILED(hres))
return hres;
}
}
break;
}
case STAT_TRY: {
try_statement_t *try_stat = (try_statement_t*)stat;
hres = visit_statement(ctx, NULL, try_stat->try_statement);
if(FAILED(hres))
return hres;
if(try_stat->catch_block) {
hres = visit_statement(ctx, NULL, try_stat->catch_block->statement);
if(FAILED(hres))
return hres;
}
if(try_stat->finally_statement)
hres = visit_statement(ctx, NULL, try_stat->finally_statement);
break;
}
case STAT_VAR:
hres = visit_variable_list(ctx, ((var_statement_t*)stat)->variable_list);
break;
case STAT_WHILE: {
while_statement_t *while_stat = (while_statement_t*)stat;
hres = visit_expression(ctx, while_stat->expr);
if(FAILED(hres))
return hres;
hres = visit_statement(ctx, NULL, while_stat->statement);
break;
}
case STAT_WITH: {
with_statement_t *with_stat = (with_statement_t*)stat;
hres = visit_expression(ctx, with_stat->expr);
if(FAILED(hres))
return hres;
hres = visit_statement(ctx, NULL, with_stat->statement);
break;
}
DEFAULT_UNREACHABLE;
}
pop_compiler_statement_ctx(ctx, stat_ctx);
return hres;
}
static void resolve_labels(compiler_ctx_t *ctx, unsigned off)
{
instr_t *instr;
for(instr = ctx->code->instrs+off; instr < ctx->code->instrs+ctx->code_off; instr++) {
if(instr_info[instr->op].arg1_type == ARG_ADDR && (instr->u.arg->uint & LABEL_FLAG)) {
assert((instr->u.arg->uint & ~LABEL_FLAG) < ctx->labels_cnt);
instr->u.arg->uint = ctx->labels[instr->u.arg->uint & ~LABEL_FLAG];
}
assert(instr_info[instr->op].arg2_type != ARG_ADDR);
}
ctx->labels_cnt = 0;
}
unsigned get_location_line(bytecode_t *code, unsigned loc, unsigned *char_pos)
{
unsigned line = code->start_line;
const WCHAR *nl, *p;
for(nl = p = code->source; p < code->source + loc; p++) {
if(*p != '\n') continue;
line++;
nl = p + 1;
}
*char_pos = loc - (nl - code->source);
return line;
}
void release_bytecode(bytecode_t *code)
{
unsigned i;
if(--code->ref)
return;
for(i=0; i < code->bstr_cnt; i++)
SysFreeString(code->bstr_pool[i]);
for(i=0; i < code->str_cnt; i++)
jsstr_release(code->str_pool[i]);
if(code->named_item)
release_named_item(code->named_item);
free(code->source);
heap_pool_free(&code->heap);
free(code->bstr_pool);
free(code->str_pool);
free(code->instrs);
free(code);
}
static HRESULT init_code(compiler_ctx_t *compiler, const WCHAR *source, UINT64 source_context, unsigned start_line)
{
size_t len = source ? lstrlenW(source) : 0;
if(len > INT32_MAX)
return E_OUTOFMEMORY;
compiler->code = calloc(1, sizeof(bytecode_t));
if(!compiler->code)
return E_OUTOFMEMORY;
compiler->code->ref = 1;
compiler->code->source_context = source_context;
compiler->code->start_line = start_line;
heap_pool_init(&compiler->code->heap);
compiler->code->source = malloc((len + 1) * sizeof(WCHAR));
if(!compiler->code->source) {
release_bytecode(compiler->code);
return E_OUTOFMEMORY;
}
if(len)
memcpy(compiler->code->source, source, len * sizeof(WCHAR));
compiler->code->source[len] = 0;
compiler->code->instrs = malloc(64 * sizeof(instr_t));
if(!compiler->code->instrs) {
release_bytecode(compiler->code);
return E_OUTOFMEMORY;
}
compiler->code_size = 64;
compiler->code_off = 1;
return S_OK;
}
static HRESULT compile_function(compiler_ctx_t *ctx, statement_t *source, function_expression_t *func_expr,
BOOL from_eval, function_code_t *func)
{
function_expression_t *iter;
function_local_t *local;
unsigned off, i, scope;
HRESULT hres;
TRACE("\n");
func->bytecode = ctx->code;
func->local_ref = INVALID_LOCAL_REF;
func->scope_index = 0;
ctx->func_head = ctx->func_tail = NULL;
ctx->from_eval = from_eval;
ctx->func = func;
ctx->local_scope_count = 0;
if (!alloc_local_scope(ctx, &scope))
return E_OUTOFMEMORY;
assert(!scope);
if(func_expr) {
parameter_t *param_iter;
if(func_expr->identifier) {
func->name = compiler_alloc_bstr(ctx, func_expr->identifier);
if(!func->name)
return E_OUTOFMEMORY;
}
if(func_expr->event_target) {
func->event_target = compiler_alloc_bstr(ctx, func_expr->event_target);
if(!func->event_target)
return E_OUTOFMEMORY;
}
func->source = func_expr->src_str;
func->source_len = func_expr->src_len;
for(param_iter = func_expr->parameter_list; param_iter; param_iter = param_iter->next)
func->param_cnt++;
func->params = compiler_alloc(ctx->code, func->param_cnt * sizeof(*func->params));
if(!func->params)
return E_OUTOFMEMORY;
for(param_iter = func_expr->parameter_list, i=0; param_iter; param_iter = param_iter->next, i++) {
func->params[i] = compiler_alloc_bstr(ctx, param_iter->identifier);
if(!func->params[i])
return E_OUTOFMEMORY;
}
}
for(i = func->param_cnt; i--;) {
if(!find_local(ctx, func->params[i], 0) && !alloc_local(ctx, func->params[i], -i-1, 0))
return E_OUTOFMEMORY;
}
hres = visit_block_statement(ctx, NULL, source);
if(FAILED(hres))
return hres;
func->local_scope_count = ctx->local_scope_count;
func->local_scopes = compiler_alloc(ctx->code, func->local_scope_count * sizeof(*func->local_scopes));
if(!func->local_scopes)
return E_OUTOFMEMORY;
func->variables = compiler_alloc(ctx->code, func->var_cnt * sizeof(*func->variables));
if(!func->variables)
return E_OUTOFMEMORY;
for (scope = 0; scope < func->local_scope_count; ++scope)
{
func->local_scopes[scope].locals = compiler_alloc(ctx->code,
ctx->local_scopes[scope].locals_cnt * sizeof(*func->local_scopes[scope].locals));
if(!func->local_scopes[scope].locals)
return E_OUTOFMEMORY;
func->local_scopes[scope].locals_cnt = ctx->local_scopes[scope].locals_cnt;
i = 0;
WINE_RB_FOR_EACH_ENTRY(local, &ctx->local_scopes[scope].locals, function_local_t, entry) {
func->local_scopes[scope].locals[i].name = local->name;
func->local_scopes[scope].locals[i].ref = local->ref;
if(local->ref >= 0) {
func->variables[local->ref].name = local->name;
func->variables[local->ref].func_id = -1;
}
i++;
}
assert(i == ctx->local_scopes[scope].locals_cnt);
}
func->funcs = compiler_alloc(ctx->code, func->func_cnt * sizeof(*func->funcs));
if(!func->funcs)
return E_OUTOFMEMORY;
memset(func->funcs, 0, func->func_cnt * sizeof(*func->funcs));
ctx->current_function_expr = ctx->func_head;
off = ctx->code_off;
hres = compile_block_statement(ctx, NULL, source);
if(FAILED(hres))
return hres;
resolve_labels(ctx, off);
hres = push_instr_uint(ctx, OP_ret, !from_eval);
if(FAILED(hres))
return hres;
if(TRACE_ON(jscript_disas))
dump_code(ctx, off);
func->instr_off = off;
for(iter = ctx->func_head, i=0; iter; iter = iter->next, i++) {
hres = compile_function(ctx, iter->statement_list, iter, FALSE, func->funcs+i);
if(FAILED(hres))
return hres;
func->funcs[i].scope_index = iter->scope_index;
TRACE("[%d] func %s, scope_index %u\n", i, debugstr_w(func->funcs[i].name), iter->scope_index);
if((ctx->parser->script->version < SCRIPTLANGUAGEVERSION_ES5 || iter->is_statement) &&
func->funcs[i].name && !func->funcs[i].event_target) {
local_ref_t *local_ref = lookup_local(func, func->funcs[i].name, func->funcs[i].scope_index);
func->funcs[i].local_ref = local_ref->ref;
TRACE("found ref %s %d for %s\n", debugstr_w(local_ref->name), local_ref->ref, debugstr_w(func->funcs[i].name));
if(local_ref->ref >= 0)
func->variables[local_ref->ref].func_id = i;
}
}
assert(i == func->func_cnt);
return S_OK;
}
static HRESULT parse_arguments(compiler_ctx_t *ctx, const WCHAR *args, BSTR *arg_array, unsigned *args_size)
{
const WCHAR *ptr = args, *ptr2;
unsigned arg_cnt = 0;
while(iswspace(*ptr))
ptr++;
if(!*ptr) {
if(args_size)
*args_size = 0;
return S_OK;
}
while(1) {
if(!iswalpha(*ptr) && *ptr != '_') {
FIXME("expected alpha or '_': %s\n", debugstr_w(ptr));
return E_FAIL;
}
ptr2 = ptr;
while(iswalnum(*ptr) || *ptr == '_')
ptr++;
if(*ptr && *ptr != ',' && !iswspace(*ptr)) {
FIXME("unexpected har %s\n", debugstr_w(ptr));
return E_FAIL;
}
if(arg_array) {
arg_array[arg_cnt] = compiler_alloc_bstr_len(ctx, ptr2, ptr-ptr2);
if(!arg_array[arg_cnt])
return E_OUTOFMEMORY;
}
arg_cnt++;
while(iswspace(*ptr))
ptr++;
if(!*ptr)
break;
if(*ptr != ',') {
FIXME("expected ',': %s\n", debugstr_w(ptr));
return E_FAIL;
}
ptr++;
while(iswspace(*ptr))
ptr++;
}
if(args_size)
*args_size = arg_cnt;
return S_OK;
}
static HRESULT compile_arguments(compiler_ctx_t *ctx, const WCHAR *args)
{
HRESULT hres;
hres = parse_arguments(ctx, args, NULL, &ctx->code->global_code.param_cnt);
if(FAILED(hres))
return hres;
ctx->code->global_code.params = compiler_alloc(ctx->code,
ctx->code->global_code.param_cnt * sizeof(*ctx->code->global_code.params));
if(!ctx->code->global_code.params)
return E_OUTOFMEMORY;
return parse_arguments(ctx, args, ctx->code->global_code.params, NULL);
}
HRESULT compile_script(script_ctx_t *ctx, const WCHAR *code, UINT64 source_context, unsigned start_line,
const WCHAR *args, const WCHAR *delimiter, BOOL from_eval, BOOL use_decode,
named_item_t *named_item, bytecode_t **ret)
{
compiler_ctx_t compiler = {0};
HRESULT hres;
hres = init_code(&compiler, code, source_context, start_line);
if(FAILED(hres))
return hres;
if(args) {
hres = compile_arguments(&compiler, args);
if(FAILED(hres))
return hres;
}
if(use_decode) {
hres = decode_source(compiler.code->source);
if(FAILED(hres)) {
WARN("Decoding failed\n");
return hres;
}
}
hres = script_parse(ctx, &compiler, compiler.code, delimiter, from_eval, &compiler.parser);
if(FAILED(hres)) {
release_bytecode(compiler.code);
return hres;
}
heap_pool_init(&compiler.heap);
hres = compile_function(&compiler, compiler.parser->source, NULL, from_eval, &compiler.code->global_code);
free(compiler.local_scopes);
heap_pool_free(&compiler.heap);
parser_release(compiler.parser);
if(FAILED(hres)) {
if(hres != DISP_E_EXCEPTION)
throw_error(ctx, hres, NULL);
set_error_location(ctx->ei, compiler.code, compiler.loc, IDS_COMPILATION_ERROR, NULL);
release_bytecode(compiler.code);
return DISP_E_EXCEPTION;
}
if(named_item) {
compiler.code->named_item = named_item;
named_item->ref++;
}
*ret = compiler.code;
return S_OK;
}