Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v4: - added patch (split from the next one); - allocate scopes in compiler context dynamically; - factor out alloc_local_scope() / remove_local_scope().
dlls/jscript/compile.c | 145 ++++++++++++++++++++++++++++++++++------- dlls/jscript/parser.h | 2 + dlls/jscript/parser.y | 2 + 3 files changed, 124 insertions(+), 25 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 46efd53cdb5..4eac4bc884a 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -39,6 +39,8 @@ typedef struct _statement_ctx_t {
const labelled_statement_t *labelled_stat;
+ unsigned int scope_index; + BOOL block_scope; struct _statement_ctx_t *next; } statement_ctx_t;
@@ -61,6 +63,14 @@ typedef struct _compiler_ctx_t { unsigned labels_size; unsigned labels_cnt;
+ struct + { + unsigned int locals_cnt; + unsigned int *ref_index; + } + *local_scopes; + unsigned local_scope_count; + unsigned local_scope_size; struct wine_rb_tree locals; unsigned locals_cnt;
@@ -127,6 +137,42 @@ static void dump_code(compiler_ctx_t *ctx, unsigned off) static HRESULT compile_expression(compiler_ctx_t*,expression_t*,BOOL); static HRESULT compile_statement(compiler_ctx_t*,statement_ctx_t*,statement_t*);
+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 = heap_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; + *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); @@ -1858,7 +1904,7 @@ static inline function_local_t *find_local(compiler_ctx_t *ctx, const WCHAR *nam return entry ? WINE_RB_ENTRY_VALUE(entry, function_local_t, entry) : NULL; }
-static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref) +static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref, unsigned int scope) { function_local_t *local;
@@ -1870,10 +1916,11 @@ static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref) local->ref = ref; wine_rb_put(&ctx->locals, name, &local->entry); ctx->locals_cnt++; + ctx->local_scopes[scope].locals_cnt++; return TRUE; }
-static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name) +static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name, unsigned int scope) { BSTR ident;
@@ -1884,7 +1931,7 @@ static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name) if(!ident) return FALSE;
- return alloc_local(ctx, ident, ctx->func->var_cnt++); + return alloc_local(ctx, ident, ctx->func->var_cnt++, scope); }
static HRESULT visit_function_expression(compiler_ctx_t *ctx, function_expression_t *expr) @@ -1897,7 +1944,7 @@ static HRESULT visit_function_expression(compiler_ctx_t *ctx, function_expressio if(!expr->is_statement && ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5) return S_OK;
- return alloc_variable(ctx, expr->identifier) ? S_OK : E_OUTOFMEMORY; + return alloc_variable(ctx, expr->identifier, 0) ? S_OK : E_OUTOFMEMORY; }
static HRESULT visit_expression(compiler_ctx_t *ctx, expression_t *expr) @@ -2033,11 +2080,18 @@ static HRESULT visit_expression(compiler_ctx_t *ctx, expression_t *expr) 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) { - if(!alloc_variable(ctx, iter->identifier)) - return E_OUTOFMEMORY; + 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); @@ -2049,30 +2103,51 @@ static HRESULT visit_variable_list(compiler_ctx_t *ctx, variable_declaration_t * return S_OK; }
-static HRESULT visit_statement(compiler_ctx_t*,statement_t*); +static HRESULT visit_statement(compiler_ctx_t*,statement_ctx_t *,statement_t*);
-static HRESULT visit_block_statement(compiler_ctx_t *ctx, statement_t *iter) +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, 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) + remove_local_scope(ctx, block->scope_index); + return S_OK; }
-static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat) +static HRESULT visit_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx, statement_t *stat) { HRESULT hres = S_OK;
+ if(stat_ctx) + { + stat_ctx->next = ctx->stat_ctx; + ctx->stat_ctx = stat_ctx; + } + switch(stat->type) { case STAT_BLOCK: - hres = visit_block_statement(ctx, ((block_statement_t*)stat)->stat_list); + hres = visit_block_statement(ctx, (block_statement_t*)stat, ((block_statement_t*)stat)->stat_list); break; case STAT_BREAK: case STAT_CONTINUE: @@ -2110,7 +2185,7 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat) break; }
- hres = visit_statement(ctx, for_stat->statement); + hres = visit_statement(ctx, NULL, for_stat->statement); if(FAILED(hres)) break;
@@ -2137,7 +2212,7 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat) return hres; }
- hres = visit_statement(ctx, forin_stat->statement); + hres = visit_statement(ctx, NULL, forin_stat->statement); break; } case STAT_IF: { @@ -2147,16 +2222,16 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat) if(FAILED(hres)) return hres;
- hres = visit_statement(ctx, if_stat->if_stat); + hres = visit_statement(ctx, NULL, if_stat->if_stat); if(FAILED(hres)) return hres;
if(if_stat->else_stat) - hres = visit_statement(ctx, if_stat->else_stat); + hres = visit_statement(ctx, NULL, if_stat->else_stat); break; } case STAT_LABEL: - hres = visit_statement(ctx, ((labelled_statement_t*)stat)->statement); + hres = visit_statement(ctx, NULL, ((labelled_statement_t*)stat)->statement); break; case STAT_SWITCH: { switch_statement_t *switch_stat = (switch_statement_t*)stat; @@ -2180,7 +2255,7 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *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); + hres = visit_statement(ctx, NULL, stat_iter); if(FAILED(hres)) return hres; } @@ -2190,18 +2265,18 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat) case STAT_TRY: { try_statement_t *try_stat = (try_statement_t*)stat;
- hres = visit_statement(ctx, try_stat->try_statement); + hres = visit_statement(ctx, NULL, try_stat->try_statement); if(FAILED(hres)) return hres;
if(try_stat->catch_block) { - hres = visit_statement(ctx, try_stat->catch_block->statement); + hres = visit_statement(ctx, NULL, try_stat->catch_block->statement); if(FAILED(hres)) return hres; }
if(try_stat->finally_statement) - hres = visit_statement(ctx, try_stat->finally_statement); + hres = visit_statement(ctx, NULL, try_stat->finally_statement); break; } case STAT_VAR: @@ -2214,22 +2289,37 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat) if(FAILED(hres)) return hres;
- hres = visit_statement(ctx, while_stat->statement); + hres = visit_statement(ctx, NULL, while_stat->statement); break; } case STAT_WITH: { with_statement_t *with_stat = (with_statement_t*)stat; + statement_ctx_t stat_ctx = {0, TRUE};
hres = visit_expression(ctx, with_stat->expr); if(FAILED(hres)) return hres;
- hres = visit_statement(ctx, with_stat->statement); + if (ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5) + { + if (!alloc_local_scope(ctx, &with_stat->scope_index)) + return E_OUTOFMEMORY; + + stat_ctx.scope_index = with_stat->scope_index; + } + + hres = visit_statement(ctx, &stat_ctx, with_stat->statement); break; } DEFAULT_UNREACHABLE; }
+ if(stat_ctx) + { + assert(ctx->stat_ctx == stat_ctx); + ctx->stat_ctx = stat_ctx->next; + } + return hres; }
@@ -2325,7 +2415,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, { function_expression_t *iter; function_local_t *local; - unsigned off, i; + unsigned off, i, scope; HRESULT hres;
TRACE("\n"); @@ -2337,6 +2427,10 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, ctx->func = func; ctx->locals_cnt = 0; wine_rb_init(&ctx->locals, function_local_cmp); + ctx->local_scope_count = 0; + if (!alloc_local_scope(ctx, &scope)) + return E_OUTOFMEMORY; + assert(!scope);
if(func_expr) { parameter_t *param_iter; @@ -2371,11 +2465,11 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, }
for(i = 0; i < func->param_cnt; i++) { - if(!find_local(ctx, func->params[i]) && !alloc_local(ctx, func->params[i], -i-1)) + if(!find_local(ctx, func->params[i]) && !alloc_local(ctx, func->params[i], -i-1, 0)) return E_OUTOFMEMORY; }
- hres = visit_block_statement(ctx, source->statement); + hres = visit_block_statement(ctx, NULL, source->statement); if(FAILED(hres)) return hres;
@@ -2549,6 +2643,7 @@ HRESULT compile_script(script_ctx_t *ctx, const WCHAR *code, UINT64 source_conte
heap_pool_init(&compiler.heap); hres = compile_function(&compiler, compiler.parser->source, NULL, from_eval, &compiler.code->global_code); + heap_free(compiler.local_scopes); heap_pool_free(&compiler.heap); parser_release(compiler.parser); if(FAILED(hres)) { diff --git a/dlls/jscript/parser.h b/dlls/jscript/parser.h index c4dd0752ee9..32bdc3b5186 100644 --- a/dlls/jscript/parser.h +++ b/dlls/jscript/parser.h @@ -129,6 +129,7 @@ struct _statement_t {
typedef struct { statement_t stat; + unsigned int scope_index; statement_t *stat_list; } block_statement_t;
@@ -184,6 +185,7 @@ typedef struct { statement_t stat; expression_t *expr; statement_t *statement; + unsigned int scope_index; } with_statement_t;
typedef struct { diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index 6de39e43fac..adfb0fbaf49 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -1131,6 +1131,7 @@ static statement_t *new_block_statement(parser_ctx_t *ctx, unsigned loc, stateme if(!ret) return NULL;
+ ret->scope_index = 0; ret->stat_list = list ? list->head : NULL;
return &ret->stat; @@ -1314,6 +1315,7 @@ static statement_t *new_with_statement(parser_ctx_t *ctx, unsigned loc, expressi
ret->expr = expr; ret->statement = statement; + ret->scope_index = 0;
return &ret->stat; }