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 --- v5: - do not assign scope indexes to 'with' scopes.
dlls/jscript/compile.c | 136 +++++++++++++++++++++++++++++++++-------- dlls/jscript/parser.h | 1 + dlls/jscript/parser.y | 1 + 3 files changed, 113 insertions(+), 25 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 46efd53cdb5..801ef53249c 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,7 +2289,7 @@ 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: { @@ -2224,12 +2299,18 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat) if(FAILED(hres)) return hres;
- hres = visit_statement(ctx, with_stat->statement); + hres = visit_statement(ctx, NULL, 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 +2406,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 +2418,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 +2456,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 +2634,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..f110b40a247 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;
diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index 6de39e43fac..e09de81c2bb 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;
Signed-off-by: Jacek Caban jacek@codeweavers.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v5: - remove redundant parts due to 'with' blocks do not have scope indexes anymore; - don't pass scope_index scope_push().
dlls/jscript/compile.c | 104 +++++++++++++-------- dlls/jscript/engine.c | 182 +++++++++++++++++++++++++++++++------ dlls/jscript/engine.h | 6 +- dlls/jscript/tests/lang.js | 16 ++++ dlls/mshtml/tests/es5.js | 48 +++++++++- 5 files changed, 285 insertions(+), 71 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 801ef53249c..fbbc460c861 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,7 +1623,7 @@ 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)) + if(!push_instr(ctx, OP_push_with_scope)) return E_OUTOFMEMORY;
hres = compile_statement(ctx, &stat_ctx, stat->statement); @@ -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) +static inline function_local_t *find_local(compiler_ctx_t *ctx, const WCHAR *name, unsigned int scope) { - 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) -{ - 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); @@ -2416,8 +2437,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; @@ -2456,7 +2475,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; }
@@ -2464,30 +2483,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) @@ -2495,7 +2519,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;
@@ -2518,7 +2542,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..c7f30b52ea9 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; } @@ -401,11 +416,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 = 0;
*ret = new_scope; return S_OK; @@ -428,7 +445,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 +571,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 +640,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 +684,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 +704,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 +727,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,8 +881,54 @@ 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) { IDispatch *disp; jsval_t v; @@ -840,6 +947,25 @@ static HRESULT interp_push_scope(script_ctx_t *ctx) 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, &frame->scope); + + if (FAILED(hres) || !scope_index) + return hres; + + frame->scope->scope_index = scope_index; + + return scope_init_locals(ctx); +} + /* ECMA-262 3rd Edition 12.10 */ static HRESULT interp_pop_scope(script_ctx_t *ctx) { diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index a94f1f211e4..48e2b8ea66a 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, 0,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"); });
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=92765
Your paranoid android.
=== w1064_tsign (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
=== w7u_adm (32 bit report) ===
mshtml: script.c:2838: Test failed: ReportResult failed: 80ef0001
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v5: - do not add scope_init_locals() call to interp_push_with_scope().
dlls/jscript/compile.c | 39 +++++++++++++++++++++++++--- dlls/jscript/engine.c | 56 ++++++++++++++++++++++++++++++++-------- dlls/jscript/engine.h | 2 ++ dlls/jscript/parser.h | 1 + dlls/jscript/parser.y | 1 + dlls/mshtml/tests/es5.js | 49 +++++++++++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 15 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index fbbc460c861..0862ba39b62 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->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; }
@@ -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; @@ -2434,6 +2460,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; @@ -2518,6 +2545,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)) @@ -2539,10 +2567,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 c7f30b52ea9..cb9ea5cd9ac 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -599,6 +599,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; } @@ -607,15 +610,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); }
/* @@ -910,7 +907,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) { @@ -971,6 +985,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 +3185,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 +3241,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 +3277,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 48e2b8ea66a..194e5244f12 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 f110b40a247..c92a3f8bdbd 100644 --- a/dlls/jscript/parser.h +++ b/dlls/jscript/parser.h @@ -305,6 +305,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 e09de81c2bb..4bac8c65d0c 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -1415,6 +1415,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");
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=92766
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()); +});
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=92767
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 --- v5: - add test for function definition within 'with' signle statement context.
dlls/mshtml/tests/es5.js | 83 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+)
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index dd6ea76c77a..03663917177 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1341,3 +1341,86 @@ 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()); + + /* 'with' has no effect for function defined in a single statement context. */ + let w = 8; + with({w:10, value:11}) + function with_function() + { + var except + + ok(w == 8, "w != 8"); + except = false; + try + { + ok(value === undefined, "value is defined"); + } + catch(e) + { + except = true; + } + ok(except, "with_function: expected exception"); + + let ret = w; + w = 9; + return ret; + } + val = with_function(); + ok(val == 8, "val != 8"); + ok(w == 9, "w != 9"); +});
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=92768
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
=== w1064_tsign (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
=== w7u_el (32 bit report) ===
mshtml: script.c:624: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1624035285114 expected 1624035285169"