Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/jscript/compile.c | 14 +++++++++----- dlls/jscript/engine.c | 10 +++++----- dlls/jscript/engine.h | 9 +++++++-- 3 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 2a17639641f..46efd53cdb5 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -2379,10 +2379,14 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, if(FAILED(hres)) return hres;
- func->locals = compiler_alloc(ctx->code, ctx->locals_cnt * sizeof(*func->locals)); - if(!func->locals) + func->local_scope_count = 1; + func->local_scopes = compiler_alloc(ctx->code, func->local_scope_count * sizeof(*func->local_scopes)); + if(!func->local_scopes) return E_OUTOFMEMORY; - func->locals_cnt = ctx->locals_cnt; + func->local_scopes[0].locals = compiler_alloc(ctx->code, ctx->locals_cnt * sizeof(*func->local_scopes[0].locals)); + if(!func->local_scopes[0].locals) + return E_OUTOFMEMORY; + func->local_scopes[0].locals_cnt = ctx->locals_cnt;
func->variables = compiler_alloc(ctx->code, func->var_cnt * sizeof(*func->variables)); if(!func->variables) @@ -2390,8 +2394,8 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
i = 0; WINE_RB_FOR_EACH_ENTRY(local, &ctx->locals, function_local_t, entry) { - func->locals[i].name = local->name; - func->locals[i].ref = local->ref; + func->local_scopes[0].locals[i].name = local->name; + func->local_scopes[0].locals[i].ref = local->ref; if(local->ref >= 0) { func->variables[local->ref].name = local->name; func->variables[local->ref].func_id = -1; diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index e0a6e10e98b..c6ca3a9c10d 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -578,13 +578,12 @@ static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BO
frame->base_scope->frame = NULL;
- for(i = 0; i < frame->function->locals_cnt; i++) { - hres = jsdisp_propput_name(frame->variable_obj, frame->function->locals[i].name, - ctx->stack[local_off(frame, frame->function->locals[i].ref)]); + for(i = 0; i < frame->function->local_scopes[0].locals_cnt; i++) { + hres = jsdisp_propput_name(frame->variable_obj, frame->function->local_scopes[0].locals[i].name, + ctx->stack[local_off(frame, frame->function->local_scopes[0].locals[i].ref)]); if(FAILED(hres)) return hres; } - return S_OK; }
@@ -630,7 +629,8 @@ static int __cdecl local_ref_cmp(const void *key, const void *ref)
local_ref_t *lookup_local(const function_code_t *function, const WCHAR *identifier) { - return bsearch(identifier, function->locals, function->locals_cnt, sizeof(*function->locals), local_ref_cmp); + return bsearch(identifier, function->local_scopes[0].locals, function->local_scopes[0].locals_cnt, + sizeof(*function->local_scopes[0].locals), local_ref_cmp); }
/* ECMA-262 3rd Edition 10.1.4 */ diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index 442e7334c41..a94f1f211e4 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -147,6 +147,11 @@ typedef struct {
#define INVALID_LOCAL_REF 0x7fffffff
+typedef struct { + unsigned locals_cnt; + local_ref_t *locals; +} local_ref_scopes_t; + typedef struct _function_code_t { BSTR name; int local_ref; @@ -168,8 +173,8 @@ typedef struct _function_code_t { unsigned param_cnt; BSTR *params;
- unsigned locals_cnt; - local_ref_t *locals; + local_ref_scopes_t *local_scopes; + unsigned local_scope_count;
bytecode_t *bytecode; } function_code_t;
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; }
Hi Paul,
Patches look mostly good to me now, I have one doubt through:
On 6/17/21 11:25 PM, Paul Gofman wrote:
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;
}
Do we need to alloc local scope for with statement? They only use passed object for the scope and, unless I'm missing something, we should never need this local scope anyway.
Thanks, Jacek
Hello Jacek,
On 6/18/21 13:16, Jacek Caban wrote:
Hi Paul,
Patches look mostly good to me now, I have one doubt through:
On 6/17/21 11:25 PM, Paul Gofman wrote:
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; +Â Â Â Â Â Â Â }
Do we need to alloc local scope for with statement? They only use passed object for the scope and, unless I'm missing something, we should never need this local scope anyway.
I recall it was needed for something at some earlier version of the patches, but I checked now, and it actually seems redundant. More so, I tested the function definition under 'with' without explicit block, and it revealed some bugs WRT current 'with' scope enumeration handling, as well that the 'with' doesn't affect the function definition ('with' scope shouldn't be attached to the function).
So, I will add the test and remove the scope enumeration for 'with' and dependent parts.
I also tried testing:
with({w:10}) Â Â Â Â Â Â Â let e = w
But figured that is syntax error. LexicalDeclaration shouldn't be actually a part of Statement but rather a part of a new Declaration statement which should be referenced from Block statement declaration. I suggest I fix that after this patch series.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v4: - remove a TRACE in bind_local(); - do not create block scope variables object for each block with locals; - introcude distinct opcodes for with and block scope pushes (and introduce a separate scope_init_locals() function to use that also from interp_push_with_scope() in the next patch; - fix local refs handling for detached vars in stack_topn_exprval(): - don't search for function parameters in the block and with scopes; - do not omit base_scope in search; - refactor detach_scope_chain() changes for better code structure and saner diff in the next patch.
dlls/jscript/compile.c | 106 ++++++++++++-------- dlls/jscript/engine.c | 192 ++++++++++++++++++++++++++++++------- dlls/jscript/engine.h | 6 +- dlls/jscript/tests/lang.js | 16 ++++ dlls/mshtml/tests/es5.js | 48 +++++++++- 5 files changed, 291 insertions(+), 77 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 4eac4bc884a..8003725e35a 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -65,14 +65,13 @@ typedef struct _compiler_ctx_t {
struct { + struct wine_rb_tree locals; 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;
statement_ctx_t *stat_ctx; function_code_t *func; @@ -137,6 +136,12 @@ 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 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; @@ -154,6 +159,7 @@ static BOOL alloc_local_scope(compiler_ctx_t *ctx, unsigned int *scope_index)
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; @@ -485,10 +491,19 @@ static BOOL bind_local(compiler_ctx_t *ctx, const WCHAR *identifier, int *ret_re
for(iter = ctx->stat_ctx; iter; iter = iter->next) { if(iter->using_scope) - return FALSE; + { + 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); + ref = lookup_local(ctx->func, identifier, 0); if(!ref) return FALSE;
@@ -1157,18 +1172,33 @@ static inline BOOL is_loop_statement(statement_type_t type) }
/* ECMA-262 3rd Edition 12.1 */ -static HRESULT compile_block_statement(compiler_ctx_t *ctx, statement_t *iter) +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, NULL, 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; }
@@ -1184,8 +1214,6 @@ static HRESULT compile_variable_list(compiler_ctx_t *ctx, variable_declaration_t if(!iter->expr) continue;
- if (iter->block_scope) - FIXME("Block scope variables are not supported.\n"); if (iter->constant) FIXME("Constant variables are not supported.\n");
@@ -1595,8 +1623,8 @@ static HRESULT compile_with_statement(compiler_ctx_t *ctx, with_statement_t *sta if(FAILED(hres)) return hres;
- if(!push_instr(ctx, OP_push_scope)) - return E_OUTOFMEMORY; + if(FAILED(hres = push_instr_uint(ctx, OP_push_with_scope, stat->scope_index))) + return hres;
hres = compile_statement(ctx, &stat_ctx, stat->statement); if(FAILED(hres)) @@ -1833,7 +1861,7 @@ static HRESULT compile_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx,
switch(stat->type) { case STAT_BLOCK: - hres = compile_block_statement(ctx, ((block_statement_t*)stat)->stat_list); + 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); @@ -1892,15 +1920,9 @@ static HRESULT compile_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx, return hres; }
-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 inline function_local_t *find_local(compiler_ctx_t *ctx, const WCHAR *name) +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->locals, name); + 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; }
@@ -1914,8 +1936,7 @@ static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref, unsigned int sc
local->name = name; local->ref = ref; - wine_rb_put(&ctx->locals, name, &local->entry); - ctx->locals_cnt++; + wine_rb_put(&ctx->local_scopes[scope].locals, name, &local->entry); ctx->local_scopes[scope].locals_cnt++; return TRUE; } @@ -1924,7 +1945,7 @@ static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name, unsigned int { BSTR ident;
- if(find_local(ctx, name)) + if(find_local(ctx, name, scope)) return TRUE;
ident = compiler_alloc_bstr(ctx, name); @@ -2425,8 +2446,6 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, ctx->func_head = ctx->func_tail = NULL; ctx->from_eval = from_eval; 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; @@ -2465,7 +2484,7 @@ 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, 0)) + if(!find_local(ctx, func->params[i], 0) && !alloc_local(ctx, func->params[i], -i-1, 0)) return E_OUTOFMEMORY; }
@@ -2473,30 +2492,35 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, if(FAILED(hres)) return hres;
- func->local_scope_count = 1; + 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->local_scopes[0].locals = compiler_alloc(ctx->code, ctx->locals_cnt * sizeof(*func->local_scopes[0].locals)); - if(!func->local_scopes[0].locals) - return E_OUTOFMEMORY; - func->local_scopes[0].locals_cnt = ctx->locals_cnt;
func->variables = compiler_alloc(ctx->code, func->var_cnt * sizeof(*func->variables)); if(!func->variables) return E_OUTOFMEMORY;
- i = 0; - WINE_RB_FOR_EACH_ENTRY(local, &ctx->locals, function_local_t, entry) { - func->local_scopes[0].locals[i].name = local->name; - func->local_scopes[0].locals[i].ref = local->ref; - if(local->ref >= 0) { - func->variables[local->ref].name = local->name; - func->variables[local->ref].func_id = -1; + 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++; } - i++; + assert(i == ctx->local_scopes[scope].locals_cnt); } - assert(i == ctx->locals_cnt);
func->funcs = compiler_alloc(ctx->code, func->func_cnt * sizeof(*func->funcs)); if(!func->funcs) @@ -2504,7 +2528,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, memset(func->funcs, 0, func->func_cnt * sizeof(*func->funcs));
off = ctx->code_off; - hres = compile_block_statement(ctx, source->statement); + hres = compile_block_statement(ctx, NULL, source->statement); if(FAILED(hres)) return hres;
@@ -2527,7 +2551,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, TRACE("[%d] func %s\n", i, debugstr_w(func->funcs[i].name)); 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); + local_ref_t *local_ref = lookup_local(func, func->funcs[i].name, 0); 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) diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index c6ca3a9c10d..b89472c2cc1 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -205,30 +205,45 @@ static BOOL stack_topn_exprval(script_ctx_t *ctx, unsigned n, exprval_t *r) switch(jsval_type(v)) { case JSV_NUMBER: { call_frame_t *frame = ctx->call_ctx; + scope_chain_t *scope; unsigned off = get_number(v);
if(!frame->base_scope->frame && off >= frame->arguments_off) { DISPID id; BSTR name; - HRESULT hres; + HRESULT hres = E_FAIL;
/* Got stack reference in deoptimized code. Need to convert it back to variable object reference. */
assert(off < frame->variables_off + frame->function->var_cnt); - name = off >= frame->variables_off - ? frame->function->variables[off - frame->variables_off].name - : frame->function->params[off - frame->arguments_off]; - hres = jsdisp_get_id(ctx->call_ctx->base_scope->jsobj, name, 0, &id); - if(FAILED(hres)) { - r->type = EXPRVAL_INVALID; - r->u.hres = hres; - return FALSE; + if (off >= frame->variables_off) + { + name = frame->function->variables[off - frame->variables_off].name; + scope = frame->scope; + } + else + { + name = frame->function->params[off - frame->arguments_off]; + scope = frame->base_scope; }
- *stack_top_ref(ctx, n+1) = jsval_obj(jsdisp_addref(frame->base_scope->jsobj)); + while (1) + { + if (scope->jsobj && SUCCEEDED(hres = jsdisp_get_id(scope->jsobj, name, 0, &id))) + break; + if (scope == frame->base_scope) + { + r->type = EXPRVAL_INVALID; + r->u.hres = hres; + return FALSE; + } + scope = scope->next; + } + + *stack_top_ref(ctx, n+1) = jsval_obj(jsdisp_addref(scope->jsobj)); *stack_top_ref(ctx, n) = jsval_number(id); r->type = EXPRVAL_IDREF; - r->u.idref.disp = frame->base_scope->obj; + r->u.idref.disp = scope->obj; r->u.idref.id = id; return TRUE; } @@ -391,7 +406,8 @@ static inline void clear_acc(script_ctx_t *ctx) ctx->acc = jsval_undefined(); }
-static HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj, scope_chain_t **ret) +static HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj, + unsigned int scope_index, scope_chain_t **ret) { scope_chain_t *new_scope;
@@ -401,11 +417,13 @@ static HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj,
new_scope->ref = 1;
- IDispatch_AddRef(obj); + if (obj) + IDispatch_AddRef(obj); new_scope->jsobj = jsobj; new_scope->obj = obj; new_scope->frame = NULL; new_scope->next = scope ? scope_addref(scope) : NULL; + new_scope->scope_index = scope_index;
*ret = new_scope; return S_OK; @@ -428,7 +446,8 @@ void scope_release(scope_chain_t *scope) if(scope->next) scope_release(scope->next);
- IDispatch_Release(scope->obj); + if (scope->obj) + IDispatch_Release(scope->obj); heap_free(scope); }
@@ -553,13 +572,59 @@ HRESULT jsval_strict_equal(jsval_t lval, jsval_t rval, BOOL *ret) return S_OK; }
+static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t *scope) +{ + unsigned int i, index; + HRESULT hres; + + if (!scope->frame) + return S_OK; + + assert(scope->frame == frame); + scope->frame = NULL; + + if (!scope->jsobj) + { + assert(!scope->obj); + + if (FAILED(hres = create_object(ctx, NULL, &scope->jsobj))) + return hres; + scope->obj = to_disp(scope->jsobj); + } + + index = scope->scope_index; + for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++) + { + WCHAR *name = frame->function->local_scopes[index].locals[i].name; + int ref = frame->function->local_scopes[index].locals[i].ref; + + if (FAILED(hres = jsdisp_propput_name(scope->jsobj, name, ctx->stack[local_off(frame, ref)]))) + return hres; + } + return S_OK; +} + +static HRESULT detach_scope_chain(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t *scope) +{ + HRESULT hres; + + while (1) + { + if ((hres = detach_scope(ctx, frame, scope))) + return hres; + if (scope == frame->base_scope) + break; + scope = scope->next; + } + return S_OK; +} + /* * Transfers local variables from stack to variable object. * It's slow, so we want to avoid it as much as possible. */ static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BOOL from_release) { - unsigned i; HRESULT hres;
if(!frame->base_scope || !frame->base_scope->frame) @@ -576,15 +641,8 @@ static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BO return hres; }
- frame->base_scope->frame = NULL; - - for(i = 0; i < frame->function->local_scopes[0].locals_cnt; i++) { - hres = jsdisp_propput_name(frame->variable_obj, frame->function->local_scopes[0].locals[i].name, - ctx->stack[local_off(frame, frame->function->local_scopes[0].locals[i].ref)]); - if(FAILED(hres)) - return hres; - } - return S_OK; + TRACE("detaching scope chain %p, frame %p.\n", ctx->call_ctx->scope, frame); + return detach_scope_chain(ctx, frame, ctx->call_ctx->scope); }
static BOOL lookup_global_members(script_ctx_t *ctx, BSTR identifier, exprval_t *ret) @@ -627,10 +685,10 @@ static int __cdecl local_ref_cmp(const void *key, const void *ref) return wcscmp((const WCHAR*)key, ((const local_ref_t*)ref)->name); }
-local_ref_t *lookup_local(const function_code_t *function, const WCHAR *identifier) +local_ref_t *lookup_local(const function_code_t *function, const WCHAR *identifier, unsigned int scope) { - return bsearch(identifier, function->local_scopes[0].locals, function->local_scopes[0].locals_cnt, - sizeof(*function->local_scopes[0].locals), local_ref_cmp); + return bsearch(identifier, function->local_scopes[scope].locals, function->local_scopes[scope].locals_cnt, + sizeof(*function->local_scopes[scope].locals), local_ref_cmp); }
/* ECMA-262 3rd Edition 10.1.4 */ @@ -647,7 +705,7 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re for(scope = ctx->call_ctx->scope; scope; scope = scope->next) { if(scope->frame) { function_code_t *func = scope->frame->function; - local_ref_t *ref = lookup_local(func, identifier); + local_ref_t *ref = lookup_local(func, identifier, scope->scope_index);
if(ref) { ret->type = EXPRVAL_STACK_REF; @@ -670,6 +728,10 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re return S_OK; } } + + if (!scope->jsobj && !scope->obj) + continue; + if(scope->jsobj) hres = jsdisp_get_id(scope->jsobj, identifier, fdexNameImplicit, &id); else @@ -820,14 +882,61 @@ static HRESULT interp_forin(script_ctx_t *ctx) return S_OK; }
+static HRESULT scope_init_locals(script_ctx_t *ctx) +{ + call_frame_t *frame = ctx->call_ctx; + unsigned int i, off, index; + scope_chain_t *scope; + BOOL detached_vars; + HRESULT hres; + + scope = frame->scope; + index = scope->scope_index; + detached_vars = !(frame->base_scope && frame->base_scope->frame); + + if (!detached_vars) + { + assert(frame->base_scope->frame == frame); + frame->scope->frame = ctx->call_ctx; + } + else if (!scope->jsobj) + { + assert(!scope->obj); + if (FAILED(hres = create_object(ctx, NULL, &scope->jsobj))) + return hres; + scope->obj = to_disp(scope->jsobj); + } + + for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++) + { + WCHAR *name = frame->function->local_scopes[index].locals[i].name; + int ref = frame->function->local_scopes[index].locals[i].ref; + jsval_t val = jsval_undefined(); + + if (detached_vars) + { + if (FAILED(hres = jsdisp_propput_name(scope->jsobj, name, val))) + return hres; + } + else + { + off = local_off(frame, ref); + jsval_release(ctx->stack[off]); + ctx->stack[off] = val; + } + } + return S_OK; +} + /* ECMA-262 3rd Edition 12.10 */ -static HRESULT interp_push_scope(script_ctx_t *ctx) +static HRESULT interp_push_with_scope(script_ctx_t *ctx) { + unsigned int scope_index = get_op_uint(ctx, 0); IDispatch *disp; jsval_t v; HRESULT hres;
- TRACE("\n"); + TRACE("scope_index %u.\n", scope_index);
v = stack_pop(ctx); hres = to_object(ctx, v, &disp); @@ -835,11 +944,28 @@ static HRESULT interp_push_scope(script_ctx_t *ctx) if(FAILED(hres)) return hres;
- hres = scope_push(ctx->call_ctx->scope, to_jsdisp(disp), disp, &ctx->call_ctx->scope); + hres = scope_push(ctx->call_ctx->scope, to_jsdisp(disp), disp, scope_index, &ctx->call_ctx->scope); IDispatch_Release(disp); return hres; }
+/* ECMA-262 10th Edition 13.3.1 */ +static HRESULT interp_push_block_scope(script_ctx_t *ctx) +{ + unsigned int scope_index = get_op_uint(ctx, 0); + call_frame_t *frame = ctx->call_ctx; + HRESULT hres; + + TRACE("scope_index %u.\n", scope_index); + + hres = scope_push(ctx->call_ctx->scope, NULL, NULL, scope_index, &frame->scope); + + if (FAILED(hres) || !scope_index) + return hres; + + return scope_init_locals(ctx); +} + /* ECMA-262 3rd Edition 12.10 */ static HRESULT interp_pop_scope(script_ctx_t *ctx) { @@ -1036,7 +1162,7 @@ static HRESULT interp_enter_catch(script_ctx_t *ctx) hres = jsdisp_propput_name(scope_obj, ident, v); jsval_release(v); if(SUCCEEDED(hres)) - hres = scope_push(ctx->call_ctx->scope, scope_obj, to_disp(scope_obj), &ctx->call_ctx->scope); + hres = scope_push(ctx->call_ctx->scope, scope_obj, to_disp(scope_obj), 0, &ctx->call_ctx->scope); jsdisp_release(scope_obj); return hres; } @@ -3032,7 +3158,7 @@ static HRESULT setup_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t
frame->pop_variables = i;
- hres = scope_push(scope_chain, variable_object, to_disp(variable_object), &scope); + hres = scope_push(scope_chain, variable_object, to_disp(variable_object), 0, &scope); if(FAILED(hres)) { stack_popn(ctx, ctx->stack_top - orig_stack); return hres; diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index a94f1f211e4..65ccba7eaa2 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -75,7 +75,8 @@ X(preinc, 1, ARG_INT, 0) \ X(push_acc, 1, 0,0) \ X(push_except,1, ARG_ADDR, ARG_UINT) \ - X(push_scope, 1, 0,0) \ + X(push_block_scope, 1, ARG_UINT, 0) \ + X(push_with_scope, 1, ARG_UINT, 0) \ X(regexp, 1, ARG_STR, ARG_UINT) \ X(rshift, 1, 0,0) \ X(rshift2, 1, 0,0) \ @@ -180,7 +181,7 @@ typedef struct _function_code_t { } function_code_t;
IDispatch *lookup_global_host(script_ctx_t*) DECLSPEC_HIDDEN; -local_ref_t *lookup_local(const function_code_t*,const WCHAR*) DECLSPEC_HIDDEN; +local_ref_t *lookup_local(const function_code_t*,const WCHAR*,unsigned int) DECLSPEC_HIDDEN;
struct _bytecode_t { LONG ref; @@ -222,6 +223,7 @@ typedef struct _scope_chain_t { LONG ref; jsdisp_t *jsobj; IDispatch *obj; + unsigned int scope_index; struct _call_frame_t *frame; struct _scope_chain_t *next; } scope_chain_t; diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js index 0db86c89985..ad1217d6b60 100644 --- a/dlls/jscript/tests/lang.js +++ b/dlls/jscript/tests/lang.js @@ -1586,6 +1586,22 @@ tmp.testWith = true; with(tmp) ok(testWith === true, "testWith !== true");
+function withScopeTest() +{ + var a = 3; + with({a : 2}) + { + ok(a == 2, "withScopeTest: a != 2"); + function func() + { + ok(a == 3, "withScopeTest: func: a != 3"); + } + func(); + eval('ok(a == 2, "withScopeTest: eval: a != 2");'); + } +} +withScopeTest(); + if(false) { var varTest1 = true; } diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 24906de5231..f4710348739 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1210,17 +1210,63 @@ sync_test("head_setter", function() {
sync_test("declaration_let", function() { + ok(typeof(func) === "undefined", "typeof(func) = " + typeof(func)); + with(new Object()) { + var x = false && function func() {}; + } + ok(typeof(func) === "undefined", "typeof(func) = " + typeof(func)); + + function expect_exception(func, todo) { + try { + func(); + }catch(e) { + return; + } + if (typeof todo === 'undefined' || !todo) + ok(false, "expected exception"); + else + todo_wine.ok(false, "expected exception"); + } + + function call_func(f, expected_a) + { + f(2, expected_a); + } + ok(a === undefined, "a is not undefined"); var a = 3;
{ let a = 2; + let b + + ok(typeof b === 'undefined', "b is defined"); + ok(b === undefined, "b !== undefined");
ok(a == 2, "a != 2");
a = 4; ok(a == 4, "a != 4"); + + eval('ok(a == 4, "eval: a != 4"); b = a; a = 5;') + ok(b == 4, "b != 4"); + ok(a == 5, "a != 5"); + + function func1() + { + ok(typeof b === 'undefined', "func1: b is defined"); + ok(b === undefined, "func1: should produce exception"); + let b = 1; + } + expect_exception(func1, true); + + function func2() + { + let b = 1; + ok(b == 1, "func2: b != 1"); + } + func2(); }
- todo_wine.ok(a == 3, "a != 3"); + ok(a == 3, "a != 3"); });
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=92711
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== w8adm (32 bit report) ===
mshtml: htmldoc.c:3084: Test failed: Incorrect error code: -2146697211 htmldoc.c:3089: Test failed: Page address: L"http://test.winehq.org/tests/winehq_snapshot/" htmldoc.c:5861: Test failed: expected OnChanged_1012 htmldoc.c:5862: Test failed: expected Exec_HTTPEQUIV htmldoc.c:5864: Test failed: expected Exec_SETTITLE htmldoc.c:5905: Test failed: expected FireNavigateComplete2
=== w10pro64_he (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v4: - no functional changes, reduced diff due to refactoring in the previous patch.
dlls/jscript/compile.c | 39 ++++++++++++++++++++--- dlls/jscript/engine.c | 68 ++++++++++++++++++++++++++++++---------- dlls/jscript/engine.h | 2 ++ dlls/jscript/parser.h | 1 + dlls/jscript/parser.y | 1 + dlls/mshtml/tests/es5.js | 49 +++++++++++++++++++++++++++++ 6 files changed, 140 insertions(+), 20 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 8003725e35a..9466e86afb7 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -41,6 +41,7 @@ typedef struct _statement_ctx_t {
unsigned int scope_index; BOOL block_scope; + BOOL scope_has_functions; struct _statement_ctx_t *next; } statement_ctx_t;
@@ -80,6 +81,7 @@ typedef struct _compiler_ctx_t {
function_expression_t *func_head; function_expression_t *func_tail; + function_expression_t *current_function_expr;
heap_pool_t heap; } compiler_ctx_t; @@ -979,6 +981,18 @@ static HRESULT compile_object_literal(compiler_ctx_t *ctx, property_value_expres
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->using_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; }
@@ -1957,15 +1971,27 @@ static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name, unsigned int
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, 0) ? S_OK : E_OUTOFMEMORY; + 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) @@ -2150,7 +2176,7 @@ static HRESULT visit_block_statement(compiler_ctx_t *ctx, block_statement_t *blo iter = iter->next; }
- if (needs_scope && !ctx->local_scopes[stat_ctx.scope_index].locals_cnt) + 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; @@ -2443,6 +2469,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
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; @@ -2527,6 +2554,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, 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->statement); if(FAILED(hres)) @@ -2548,10 +2576,13 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, if(FAILED(hres)) return hres;
- TRACE("[%d] func %s\n", i, debugstr_w(func->funcs[i].name)); + 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, 0); + 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) diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index b89472c2cc1..aea91ebbf35 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -600,6 +600,9 @@ static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_
if (FAILED(hres = jsdisp_propput_name(scope->jsobj, name, ctx->stack[local_off(frame, ref)]))) return hres; + if (frame->function->variables[ref].func_id != -1 && scope != frame->base_scope + && FAILED(hres = jsdisp_propput_name(frame->variable_obj, name, ctx->stack[local_off(frame, ref)]))) + return hres; } return S_OK; } @@ -608,15 +611,9 @@ static HRESULT detach_scope_chain(script_ctx_t *ctx, call_frame_t *frame, scope_ { HRESULT hres;
- while (1) - { - if ((hres = detach_scope(ctx, frame, scope))) - return hres; - if (scope == frame->base_scope) - break; - scope = scope->next; - } - return S_OK; + if (scope != frame->base_scope && FAILED(hres = detach_scope_chain(ctx, frame, scope->next))) + return hres; + return detach_scope(ctx, frame, scope); }
/* @@ -882,7 +879,7 @@ static HRESULT interp_forin(script_ctx_t *ctx) return S_OK; }
-static HRESULT scope_init_locals(script_ctx_t *ctx) +static HRESULT scope_init_locals(script_ctx_t *ctx, BOOL scope_block) { call_frame_t *frame = ctx->call_ctx; unsigned int i, off, index; @@ -894,12 +891,12 @@ static HRESULT scope_init_locals(script_ctx_t *ctx) index = scope->scope_index; detached_vars = !(frame->base_scope && frame->base_scope->frame);
- if (!detached_vars) + if (scope_block && !detached_vars) { assert(frame->base_scope->frame == frame); frame->scope->frame = ctx->call_ctx; } - else if (!scope->jsobj) + else if (scope_block && !scope->jsobj) { assert(!scope->obj); if (FAILED(hres = create_object(ctx, NULL, &scope->jsobj))) @@ -911,7 +908,24 @@ static HRESULT scope_init_locals(script_ctx_t *ctx) { WCHAR *name = frame->function->local_scopes[index].locals[i].name; int ref = frame->function->local_scopes[index].locals[i].ref; - jsval_t val = jsval_undefined(); + jsdisp_t *func_obj; + jsval_t val; + + if (frame->function->variables[ref].func_id != -1) + { + TRACE("function %s %d\n", debugstr_w(name), i); + + if (FAILED(hres = create_source_function(ctx, frame->bytecode, frame->function->funcs + + frame->function->variables[ref].func_id, ctx->call_ctx->scope, &func_obj))) + return hres; + val = jsval_obj(func_obj); + if (detached_vars && FAILED(hres = jsdisp_propput_name(frame->variable_obj, name, jsval_obj(func_obj)))) + return hres; + } + else + { + val = jsval_undefined(); + }
if (detached_vars) { @@ -946,7 +960,9 @@ static HRESULT interp_push_with_scope(script_ctx_t *ctx)
hres = scope_push(ctx->call_ctx->scope, to_jsdisp(disp), disp, scope_index, &ctx->call_ctx->scope); IDispatch_Release(disp); - return hres; + if (FAILED(hres) || !scope_index) + return hres; + return scope_init_locals(ctx, FALSE); }
/* ECMA-262 10th Edition 13.3.1 */ @@ -963,7 +979,7 @@ static HRESULT interp_push_block_scope(script_ctx_t *ctx) if (FAILED(hres) || !scope_index) return hres;
- return scope_init_locals(ctx); + return scope_init_locals(ctx, TRUE); }
/* ECMA-262 3rd Edition 12.10 */ @@ -971,6 +987,12 @@ static HRESULT interp_pop_scope(script_ctx_t *ctx) { TRACE("\n");
+ if(ctx->call_ctx->scope->ref > 1) { + HRESULT hres = detach_variable_object(ctx, ctx->call_ctx, FALSE); + if(FAILED(hres)) + ERR("Failed to detach variable object: %08x\n", hres); + } + scope_pop(&ctx->call_ctx->scope); return S_OK; } @@ -3165,7 +3187,9 @@ static HRESULT setup_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t }
for(i = 0; i < frame->function->func_cnt; i++) { - if(frame->function->funcs[i].local_ref != INVALID_LOCAL_REF) { + if(frame->function->funcs[i].local_ref != INVALID_LOCAL_REF + && !frame->function->funcs[i].scope_index) + { jsdisp_t *func_obj; unsigned off;
@@ -3219,6 +3243,12 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi if(!function->funcs[i].event_target) continue;
+ if (function->funcs[i].scope_index) + { + /* TODO: Add tests and handle in interp_push_scope(). */ + FIXME("Event target with scope index are not properly handled.\n"); + } + hres = create_source_function(ctx, bytecode, function->funcs+i, scope, &func_obj); if(FAILED(hres)) return hres; @@ -3249,6 +3279,12 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi if(function->variables[i].func_id != -1) { jsdisp_t *func_obj;
+ if (function->funcs[function->variables[i].func_id].scope_index && flags & EXEC_EVAL) + { + /* TODO: Add tests and handle in interp_push_scope(). */ + FIXME("Functions with scope index inside eval() are not properly handled.\n"); + } + hres = create_source_function(ctx, bytecode, function->funcs+function->variables[i].func_id, scope, &func_obj); if(FAILED(hres)) goto fail; diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index 65ccba7eaa2..8d91f46278b 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -177,6 +177,8 @@ typedef struct _function_code_t { local_ref_scopes_t *local_scopes; unsigned local_scope_count;
+ unsigned int scope_index; /* index of scope in the parent function where the function is defined */ + bytecode_t *bytecode; } function_code_t;
diff --git a/dlls/jscript/parser.h b/dlls/jscript/parser.h index 32bdc3b5186..df036d47fd4 100644 --- a/dlls/jscript/parser.h +++ b/dlls/jscript/parser.h @@ -306,6 +306,7 @@ typedef struct _function_expression_t { DWORD src_len; unsigned func_id; BOOL is_statement; + unsigned int scope_index;
struct _function_expression_t *next; /* for compiler */ } function_expression_t; diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index adfb0fbaf49..e5dbd316f31 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -1416,6 +1416,7 @@ static expression_t *new_function_expression(parser_ctx_t *ctx, const WCHAR *ide ret->src_str = src_str; ret->src_len = src_len; ret->is_statement = FALSE; + ret->scope_index = 0; ret->next = NULL;
return &ret->expr; diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index f4710348739..2fb115556e5 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1266,6 +1266,55 @@ sync_test("declaration_let", function() { ok(b == 1, "func2: b != 1"); } func2(); + + var w = 8; + with({w: 9}) + { + { + let c = 5 + + function func3(b, expected) + { + var b = 2 + + ok(typeof d === 'undefined', "d is defined"); + + ok(c == expected, "func3: c != expected"); + ok(w == 9, "w != 9") + ok(b == 2, "func3: b != 2"); + b = 3; + ok(b == 3, "func3: b != 3"); + ok(a == expected, "func3: a != expected"); + a = 6; + c = 6; + } + + let f3 = func3 + let f4 = function() + { + ok(a == 6, "f4: a != 6"); + } + + ok(a == 5, "tmp 2 a != 5"); + ok(c == 5, "c != 5"); + func3(1, 5) + ok(c == 6, "c != 6"); + call_func(func3, 6); + f3(1, 6) + ok(a == 6, "a != 6"); + ok(b == 4, "b != 4"); + ok(c == 6, "c != 6"); + + call_func(f4); + f4(); + } + } + { + let c = 4; + let d = 1; + + func3(1, 6); + } }
ok(a == 3, "a != 3");
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=92712
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
Based on a patch by Jacek Caban.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/mshtml/tests/es5.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 2fb115556e5..dd6ea76c77a 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1319,3 +1319,25 @@ sync_test("declaration_let", function() {
ok(a == 3, "a != 3"); }); + +sync_test("let scope instances", function() { + var a = [], i; + for(i = 0; i < 3; i++) { + a[i] = function() { return v; }; + let v = i; + } + for(i = 0; i < 3; i++) + ok(a[i]() == i, "a[" + i + "]() = " + a[i]()); + + ok(typeof f == 'undefined', "f is defined"); + + for(i = 0; i < 3; i++) { + function f() { return v; } + a[i] = f; + let v = i; + } + for(i = 0; i < 3; i++) + ok(a[i]() == i, "a[" + i + "]() = " + a[i]()); + + ok(f() == 2, "f() = " + f()); +});
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=92713
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== w1064_tsign (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w10pro64_ar (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w10pro64_he (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
Patch by Jacek Caban.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/mshtml/tests/es5.js | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index dd6ea76c77a..1915a743f56 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1341,3 +1341,59 @@ sync_test("let scope instances", function() {
ok(f() == 2, "f() = " + f()); }); + +sync_test("functions scope", function() { + function f(){ return 1; } + function f(){ return 2; } + + var f0 = f, f1, f2, f3, i, o, a = []; + ok(f0() === 2, "f0() = " + f0()); + + { + f1 = f; + function f() { return 3; } + ok(f1 === f, "f1 != f"); + ok(f0 != f1, "f0 == f1"); + } + ok(f === f1, "f != f1"); + + for(i = 0; i < 3; i++) { + a[i] = f; + function f() {} + ok(f === a[i], "f != a[i]"); + } + ok(a[0] != a[1], "a[0] == a[1]"); + ok(a[1] != a[2], "a[1] == a[2]"); + ok(f === a[2], "f != a[2]"); + + { + f2 = f; + ok(f() === 4, "f() = " + f()); + function f() { return 4; } + + { + f3 = f; + ok(f() === 5, "f() = " + f()); + function f() { return 5;} + } + ok(f() === 4, "f() = " + f()); + ok(f === f2, "f != f2"); + } + ok(f === f3, "f != f3"); + + with(o = {f: 1}) { + ok(f === 1, "f != 1"); + { + ok(f() === 6, "f() = " + f()); + function f() { return 6; } + } + ok(f === 1, "f != 1"); + delete o.f; + ok(f() === 6, "f() = " + f()); + } + + if(false) { + function f() { throw "unexpected call"; } + } + ok(f() === 6, "f() = " + f()); +});
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=92714
Your paranoid android.
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== w8 (32 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w1064_tsign (32 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w7u_el (32 bit report) ===
mshtml: script.c:624: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1623990683549 expected 1623990683611"