+static int local_cmp(const void *key, const void *ref)
+{
+ return strcmpW((const WCHAR*)key, ((const local_ref_t*)ref)->name);
+}
+
+static inline local_ref_t *find_local(compiler_ctx_t *ctx, const WCHAR *name)
+{
+ return bsearch(name, ctx->locals_buf, ctx->locals_cnt, sizeof(*ctx->locals_buf), local_cmp);
+}
+
+static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref)
+{
+ unsigned i;
+
+ if(!ctx->locals_buf_size) {
+ ctx->locals_buf = heap_alloc(4 * sizeof(*ctx->locals_buf));
+ if(!ctx->locals_buf)
+ return FALSE;
+ ctx->locals_buf_size = 4;
+ }else if(ctx->locals_buf_size == ctx->locals_cnt) {
+ local_ref_t *new_buf = heap_realloc(ctx->locals_buf, ctx->locals_buf_size * 2 * sizeof(*ctx->locals_buf));
+ if(!new_buf)
+ return FALSE;
+ ctx->locals_buf = new_buf;
+ ctx->locals_buf_size *= 2;
+ }
+
+ for(i = 0; i < ctx->locals_cnt; i++) {
+ if(strcmpW(ctx->locals_buf[i].name, name) > 0) {
+ memmove(ctx->locals_buf + i+1, ctx->locals_buf + i, (ctx->locals_cnt - i) * sizeof(*ctx->locals_buf));
+ break;
+ }
+ }
+
+ ctx->locals_buf[i].name = name;
+ ctx->locals_buf[i].ref = ref;
+ ctx->locals_cnt++;
+ return TRUE;
+}
+
+static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name)
+{
+ BSTR ident;
+
+ if(find_local(ctx, name))
+ return TRUE;
+
+ ident = compiler_alloc_bstr(ctx, name);
+ if(!ident)
+ return FALSE;
+
+ return alloc_local(ctx, ident, ctx->func->var_cnt++);
+}
+
+static BOOL visit_function_expression(compiler_ctx_t *ctx, function_expression_t *expr)
+{
+ expr->func_id = ctx->func->func_cnt++;
+ ctx->func_tail = ctx->func_tail ? (ctx->func_tail->next = expr) : (ctx->func_head = expr);
+
+ return !expr->identifier || expr->event_target || alloc_variable(ctx, expr->identifier);
+}
+
+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:
+ 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: {
+ prop_val_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;
+ HRESULT hres;
+
+ for(iter = list; iter; iter = iter->next) {
+ if(!alloc_variable(ctx, iter->identifier))
+ 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_t*);
+
+static HRESULT visit_block_statement(compiler_ctx_t *ctx, statement_t *iter)
+{
+ HRESULT hres;
+
+ while(iter) {
+ hres = visit_statement(ctx, iter);
+ if(FAILED(hres))
+ return hres;
+
+ iter = iter->next;
+ }
+
+ return S_OK;
+}
+
+static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat)
+{
+ HRESULT hres = S_OK;
+
+ switch(stat->type) {
+ case STAT_BLOCK:
+ hres = visit_block_statement(ctx, ((block_statement_t*)stat)->stat_list);
+ break;
+ case STAT_BREAK:
+ case STAT_CONTINUE:
+ case STAT_EMPTY:
+ break;
+ case STAT_EXPR:
+ 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: {
+ for_statement_t *for_stat = (for_statement_t*)stat;
+
+ if(for_stat->variable_list)
+ 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))
+ break;
+
+ if(for_stat->expr) {
+ hres = visit_expression(ctx, for_stat->expr);
+ if(FAILED(hres))
+ break;
+ }
+
+ hres = visit_statement(ctx, for_stat->statement);
+ if(FAILED(hres))
+ break;
+
+ if(for_stat->end_expr)
+ hres = visit_expression(ctx, for_stat->end_expr);
+ 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, 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, if_stat->if_stat);
+ if(FAILED(hres))
+ return hres;
+
+ if(if_stat->else_stat)
+ hres = visit_statement(ctx, if_stat->else_stat);
+ break;
+ }
+ case STAT_LABEL:
+ hres = visit_statement(ctx, ((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, stat_iter);
+ if(FAILED(hres))
+ return hres;
+ }
+ }
+ break;
+ }
+ case STAT_TRY: {
+ try_statement_t *try_stat = (try_statement_t*)stat;
+
+ hres = visit_statement(ctx, try_stat->try_statement);
+ if(FAILED(hres))
+ return hres;
+
+ if(try_stat->catch_block) {
+ hres = visit_statement(ctx, try_stat->catch_block->statement);
+ if(FAILED(hres))
+ return hres;
+ }
+
+ if(try_stat->finally_statement)
+ hres = visit_statement(ctx, 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, 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, with_stat->statement);
+ break;
+ }
+ DEFAULT_UNREACHABLE;
+ }
+
+ return hres;
+}
+