Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v2: - don't allow 'const' variable declaration in compat mode.
dlls/jscript/compile.c | 5 +++++ dlls/jscript/lex.c | 2 ++ dlls/jscript/parser.h | 1 + dlls/jscript/parser.y | 37 ++++++++++++++++++++++++++++++++----- dlls/jscript/tests/lang.js | 34 ++++++++++++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 17 +++++++++++++++++ 6 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 9bb9ff13d14..2a17639641f 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -1138,6 +1138,11 @@ 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"); + hres = emit_identifier_ref(ctx, iter->identifier, 0); if(FAILED(hres)) return hres; diff --git a/dlls/jscript/lex.c b/dlls/jscript/lex.c index efed1038a95..695615721a7 100644 --- a/dlls/jscript/lex.c +++ b/dlls/jscript/lex.c @@ -41,6 +41,7 @@ static const struct { {L"break", kBREAK, TRUE}, {L"case", kCASE}, {L"catch", kCATCH}, + {L"const", kCONST}, {L"continue", kCONTINUE, TRUE}, {L"default", kDEFAULT}, {L"delete", kDELETE}, @@ -54,6 +55,7 @@ static const struct { {L"if", kIF}, {L"in", kIN}, {L"instanceof", kINSTANCEOF}, + {L"let", kLET, FALSE, SCRIPTLANGUAGEVERSION_ES5}, {L"new", kNEW}, {L"null", kNULL}, {L"return", kRETURN, TRUE}, diff --git a/dlls/jscript/parser.h b/dlls/jscript/parser.h index b88a52af242..c4dd0752ee9 100644 --- a/dlls/jscript/parser.h +++ b/dlls/jscript/parser.h @@ -95,6 +95,7 @@ literal_t *new_boolean_literal(parser_ctx_t*,BOOL) DECLSPEC_HIDDEN;
typedef struct _variable_declaration_t { const WCHAR *identifier; + BOOL block_scope, constant; expression_t *expr;
struct _variable_declaration_t *next; diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index f11119143b8..fd8ea7e7aa7 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -87,7 +87,7 @@ static variable_list_t *variable_list_add(parser_ctx_t*,variable_list_t*,variabl
static void *new_statement(parser_ctx_t*,statement_type_t,size_t,unsigned); static statement_t *new_block_statement(parser_ctx_t*,unsigned,statement_list_t*); -static statement_t *new_var_statement(parser_ctx_t*,unsigned,variable_list_t*); +static statement_t *new_var_statement(parser_ctx_t*,BOOL,BOOL,unsigned,variable_list_t*); static statement_t *new_expression_statement(parser_ctx_t*,unsigned,expression_t*); static statement_t *new_if_statement(parser_ctx_t*,unsigned,expression_t*,statement_t*,statement_t*); static statement_t *new_while_statement(parser_ctx_t*,unsigned,BOOL,expression_t*,statement_t*); @@ -168,8 +168,9 @@ static source_elements_t *source_elements_add_statement(source_elements_t*,state }
/* keywords */ -%token <identifier> kBREAK kCASE kCATCH kCONTINUE kDEFAULT kDELETE kDO kELSE kFUNCTION kIF kFINALLY kFOR kGET kIN kSET -%token <identifier> kINSTANCEOF kNEW kNULL kRETURN kSWITCH kTHIS kTHROW kTRUE kFALSE kTRY kTYPEOF kVAR kVOID kWHILE kWITH +%token <identifier> kBREAK kCASE kCATCH kCONST kCONTINUE kDEFAULT kDELETE kDO kELSE kFUNCTION kIF kFINALLY kFOR +%token <identifier> kGET kIN kLET kSET kINSTANCEOF kNEW kNULL kRETURN kSWITCH kTHIS kTHROW kTRUE kFALSE +%token <identifier> kTRY kTYPEOF kVAR kVOID kWHILE kWITH %token tANDAND tOROR tINC tDEC tHTMLCOMMENT kDIVEQ kDCOL
/* tokens */ @@ -182,6 +183,7 @@ static source_elements_t *source_elements_add_statement(source_elements_t*,state %type <source_elements> FunctionBody %type <statement> Statement %type <statement> Block +%type <statement> LexicalDeclaration %type <statement> VariableStatement %type <statement> EmptyStatement %type <statement> ExpressionStatement @@ -291,6 +293,7 @@ FormalParameterList_opt /* ECMA-262 3rd Edition 12 */ Statement : Block { $$ = $1; } + | LexicalDeclaration { $$ = $1; } | VariableStatement { $$ = $1; } | EmptyStatement { $$ = $1; } | FunctionExpression { $$ = new_expression_statement(ctx, @$, $1); } @@ -322,10 +325,25 @@ Block : '{' StatementList '}' { $$ = new_block_statement(ctx, @2, $2); } | '{' '}' { $$ = new_block_statement(ctx, @$, NULL); }
+/* ECMA-262 10th Edition 13.3.1, TODO: BindingList*/ +LexicalDeclaration + : kLET VariableDeclarationList semicolon_opt + { $$ = new_var_statement(ctx, TRUE, FALSE, @$, $2); } + | kCONST VariableDeclarationList semicolon_opt + { + if(ctx->script->version < SCRIPTLANGUAGEVERSION_ES5) { + WARN("const var declaration in legacy mode.\n", + debugstr_w($1)); + set_error(ctx, @$, JS_E_SYNTAX); + YYABORT; + } + $$ = new_var_statement(ctx, TRUE, TRUE, @$, $2); + } + /* ECMA-262 3rd Edition 12.2 */ VariableStatement : kVAR VariableDeclarationList semicolon_opt - { $$ = new_var_statement(ctx, @$, $2); } + { $$ = new_var_statement(ctx, FALSE, FALSE, @$, $2); }
/* ECMA-262 3rd Edition 12.2 */ VariableDeclarationList @@ -834,6 +852,7 @@ ReservedAsIdentifier : kBREAK { $$ = $1; } | kCASE { $$ = $1; } | kCATCH { $$ = $1; } + | kCONST { $$ = $1; } | kCONTINUE { $$ = $1; } | kDEFAULT { $$ = $1; } | kDELETE { $$ = $1; } @@ -847,6 +866,7 @@ ReservedAsIdentifier | kIF { $$ = $1; } | kIN { $$ = $1; } | kINSTANCEOF { $$ = $1; } + | kLET { $$ = $1; } | kNEW { $$ = $1; } | kNULL { $$ = $1; } | kRETURN { $$ = $1; } @@ -1144,8 +1164,10 @@ static variable_list_t *variable_list_add(parser_ctx_t *ctx, variable_list_t *li return list; }
-static statement_t *new_var_statement(parser_ctx_t *ctx, unsigned loc, variable_list_t *variable_list) +static statement_t *new_var_statement(parser_ctx_t *ctx, BOOL block_scope, BOOL constant, unsigned loc, + variable_list_t *variable_list) { + variable_declaration_t *var; var_statement_t *ret;
ret = new_statement(ctx, STAT_VAR, sizeof(*ret), loc); @@ -1153,6 +1175,11 @@ static statement_t *new_var_statement(parser_ctx_t *ctx, unsigned loc, variable_ return NULL;
ret->variable_list = variable_list->head; + for (var = ret->variable_list; var; var = var->next) + { + var->block_scope = block_scope; + var->constant = constant; + }
return &ret->stat; } diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js index 9e6e6ad239f..0db86c89985 100644 --- a/dlls/jscript/tests/lang.js +++ b/dlls/jscript/tests/lang.js @@ -2030,3 +2030,37 @@ Math = 6; ok(Math === 6, "NaN !== 6");
reportSuccess(); + +function test_es5_keywords() { + var let = 1 + var tmp + ok(let == 1, "let != 1"); + + tmp = false + try { + eval('var var = 1;'); + } + catch(e) { + tmp = true + } + ok(tmp === true, "Expected exception for 'var var = 1;'"); + + tmp = false + try { + eval('var const = 1;'); + } + catch(e) { + tmp = true + } + ok(tmp === true, "Expected exception for 'var const = 1;'"); + + tmp = false + try { + eval('const c1 = 1;'); + } + catch(e) { + tmp = true + } + ok(tmp === true, "Expected exception for 'const c1 = 1;'"); +} +test_es5_keywords(); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 7e46848d40e..24906de5231 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1207,3 +1207,20 @@ sync_test("head_setter", function() { document.head = ""; ok(typeof(document.head) === "object", "typeof(document.head) = " + typeof(document.head)); }); + + +sync_test("declaration_let", function() { + ok(a === undefined, "a is not undefined"); + var a = 3; + + { + let a = 2; + + ok(a == 2, "a != 2"); + + a = 4; + ok(a == 4, "a != 4"); + } + + todo_wine.ok(a == 3, "a != 3"); +});
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 --- v3: - initialize new scope's locals tree in visit_statement( STAT_WITH case): fixes assertion in test from patch 6.
dlls/jscript/compile.c | 235 +++++++++++++++++++++++++++---------- dlls/jscript/engine.c | 105 ++++++++++++++--- dlls/jscript/engine.h | 5 +- dlls/jscript/parser.h | 2 + dlls/jscript/parser.y | 2 + dlls/jscript/tests/lang.js | 16 +++ dlls/mshtml/tests/es5.js | 48 +++++++- 7 files changed, 334 insertions(+), 79 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 46efd53cdb5..9ac3d221ff0 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;
@@ -48,6 +50,8 @@ typedef struct { int ref; } function_local_t;
+#define MAX_SCOPE_COUNT 1024 + typedef struct _compiler_ctx_t { parser_ctx_t *parser; bytecode_t *code; @@ -61,8 +65,14 @@ typedef struct _compiler_ctx_t { unsigned labels_size; unsigned labels_cnt;
- struct wine_rb_tree locals; - unsigned locals_cnt; + struct + { + struct wine_rb_tree locals; + unsigned int locals_cnt; + unsigned int *ref_index; + } + *local_scopes; + unsigned local_scope_count;
statement_ctx_t *stat_ctx; function_code_t *func; @@ -253,6 +263,19 @@ static HRESULT push_instr_int(compiler_ctx_t *ctx, jsop_t op, LONG arg) return S_OK; }
+static HRESULT push_instr_uint_uint(compiler_ctx_t *ctx, jsop_t op, unsigned arg1, unsigned arg2) +{ + unsigned instr; + + instr = push_instr(ctx, op); + if(!instr) + return E_OUTOFMEMORY; + + instr_ptr(ctx, instr)->u.arg[0].uint = arg1; + instr_ptr(ctx, instr)->u.arg[1].uint = arg2; + return S_OK; +} + static HRESULT push_instr_str(compiler_ctx_t *ctx, jsop_t op, jsstr_t *str) { unsigned instr; @@ -439,10 +462,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; + TRACE("iter->scope_index %d, ctx->func->local_scope_count %d.\n", iter->scope_index, ctx->func->local_scope_count); + 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;
@@ -1111,18 +1143,35 @@ 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(!push_instr(ctx, OP_new_obj)) + return E_OUTOFMEMORY; + if(FAILED(hres = push_instr_uint_uint(ctx, OP_push_scope, block->scope_index, TRUE))) + 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; }
@@ -1138,8 +1187,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");
@@ -1549,8 +1596,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_uint(ctx, OP_push_scope, stat->scope_index, FALSE))) + return hres;
hres = compile_statement(ctx, &stat_ctx, stat->statement); if(FAILED(hres)) @@ -1787,7 +1834,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); @@ -1852,13 +1899,13 @@ static int function_local_cmp(const void *key, const struct wine_rb_entry *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; }
-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;
@@ -1868,23 +1915,23 @@ static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref)
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; }
-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;
- if(find_local(ctx, name)) + if(find_local(ctx, name, scope)) return TRUE;
ident = compiler_alloc_bstr(ctx, name); if(!ident) return FALSE;
- return alloc_local(ctx, ident, ctx->func->var_cnt++); + 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,67 @@ 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; + unsigned int i; HRESULT hres;
+ needs_scope = block && ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5; + if (needs_scope) + { + if (ctx->local_scope_count > MAX_SCOPE_COUNT) + return E_OUTOFMEMORY; + + stat_ctx.scope_index = ctx->local_scope_count++; + stat_ctx.block_scope = TRUE; + ctx->local_scopes[stat_ctx.scope_index].locals_cnt = 0; + ctx->local_scopes[stat_ctx.scope_index].ref_index = &block->scope_index; + + wine_rb_init(&ctx->local_scopes[stat_ctx.scope_index].locals, function_local_cmp); + } + 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) + return S_OK; + + if (ctx->local_scopes[stat_ctx.scope_index].locals_cnt) + { + block->scope_index = stat_ctx.scope_index; + } + else + { + --ctx->local_scope_count; + memmove(&ctx->local_scopes[stat_ctx.scope_index], &ctx->local_scopes[stat_ctx.scope_index + 1], + sizeof(*ctx->local_scopes) * (ctx->local_scope_count - stat_ctx.scope_index)); + for (i = stat_ctx.scope_index; i < ctx->local_scope_count; ++i) + --*ctx->local_scopes[i].ref_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 +2201,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 +2228,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 +2238,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 +2271,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 +2281,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 +2305,36 @@ 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) + { + stat_ctx.scope_index = with_stat->scope_index = ctx->local_scope_count++; + ctx->local_scopes[stat_ctx.scope_index].locals_cnt = 0; + ctx->local_scopes[stat_ctx.scope_index].ref_index = &with_stat->scope_index; + wine_rb_init(&ctx->local_scopes[stat_ctx.scope_index].locals, function_local_cmp); + } + + 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 +2430,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"); @@ -2335,8 +2440,9 @@ 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 = 1; + ctx->local_scopes[0].locals_cnt = 0; + wine_rb_init(&ctx->local_scopes[0].locals, function_local_cmp);
if(func_expr) { parameter_t *param_iter; @@ -2371,38 +2477,43 @@ 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], 0) && !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;
- 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) @@ -2410,7 +2521,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;
@@ -2433,7 +2544,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) @@ -2548,7 +2659,11 @@ 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); + if (!(compiler.local_scopes = heap_alloc(MAX_SCOPE_COUNT * sizeof(*compiler.local_scopes)))) + hres = E_OUTOFMEMORY; + else + 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/engine.c b/dlls/jscript/engine.c index c6ca3a9c10d..877d67afb31 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -205,6 +205,7 @@ 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) { @@ -218,17 +219,26 @@ static BOOL stack_topn_exprval(script_ctx_t *ctx, unsigned n, exprval_t *r) 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); + scope = ctx->call_ctx->scope; + hres = E_FAIL; + do + { + if (scope->jsobj && SUCCEEDED(hres = jsdisp_get_id(scope->jsobj, name, 0, &id))) + break; + scope = scope->next; + } + while (scope != ctx->call_ctx->base_scope); + if(FAILED(hres)) { r->type = EXPRVAL_INVALID; r->u.hres = hres; return FALSE; }
- *stack_top_ref(ctx, n+1) = jsval_obj(jsdisp_addref(frame->base_scope->jsobj)); + *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 +401,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;
@@ -406,6 +417,7 @@ static HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj, 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; @@ -559,10 +571,35 @@ HRESULT jsval_strict_equal(jsval_t lval, jsval_t rval, BOOL *ret) */ static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BOOL from_release) { - unsigned i; + unsigned int i, index; + scope_chain_t *scope; HRESULT hres;
- if(!frame->base_scope || !frame->base_scope->frame) + if(!frame->base_scope) + return S_OK; + + TRACE("detaching scope chain %p, frame %p.\n", ctx->call_ctx->scope, frame); + + for (scope = ctx->call_ctx->scope; scope != frame->base_scope; scope = scope->next) + { + if (!scope->frame) + continue; + + assert(scope->frame == frame); + scope->frame = NULL; + + index = scope->scope_index; + + for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++) + { + hres = jsdisp_propput_name(scope->jsobj, frame->function->local_scopes[index].locals[i].name, + ctx->stack[local_off(frame, frame->function->local_scopes[index].locals[i].ref)]); + if(FAILED(hres)) + return hres; + } + } + + if(!frame->base_scope->frame) return S_OK;
TRACE("detaching %p\n", frame); @@ -627,10 +664,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 +684,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; @@ -823,11 +860,17 @@ static HRESULT interp_forin(script_ctx_t *ctx) /* ECMA-262 3rd Edition 12.10 */ static HRESULT interp_push_scope(script_ctx_t *ctx) { + unsigned int scope_index = get_op_uint(ctx, 0); + BOOL scope_block = get_op_uint(ctx, 1); + call_frame_t *frame = ctx->call_ctx; + unsigned int off; + jsdisp_t *dispex; IDispatch *disp; - jsval_t v; + unsigned int i; HRESULT hres; + jsval_t v;
- TRACE("\n"); + TRACE("scope_index %u.\n", scope_index);
v = stack_pop(ctx); hres = to_object(ctx, v, &disp); @@ -835,9 +878,39 @@ 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); + dispex = to_jsdisp(disp); + hres = scope_push(ctx->call_ctx->scope, dispex, disp, scope_index, &ctx->call_ctx->scope); IDispatch_Release(disp); - return hres; + + if (FAILED(hres) || !scope_block) + return hres; + + assert(dispex); + + if (frame->base_scope && frame->base_scope->frame) + { + assert(frame->base_scope->frame == frame); + frame->scope->frame = ctx->call_ctx; + + for(i = 0; i < frame->function->local_scopes[scope_index].locals_cnt; i++) + { + off = local_off(frame, frame->function->local_scopes[scope_index].locals[i].ref); + jsval_release(ctx->stack[off]); + ctx->stack[off] = jsval_undefined(); + } + } + else + { + for(i = 0; i < frame->function->local_scopes[scope_index].locals_cnt; i++) + { + hres = jsdisp_propput_name(dispex, frame->function->local_scopes[scope_index].locals[i].name, + jsval_undefined()); + if(FAILED(hres)) + return hres; + } + } + + return S_OK; }
/* ECMA-262 3rd Edition 12.10 */ @@ -1036,7 +1109,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 +3105,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..3656b32dd8d 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -75,7 +75,7 @@ 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_scope, 1, ARG_UINT, ARG_UINT) \ X(regexp, 1, ARG_STR, ARG_UINT) \ X(rshift, 1, 0,0) \ X(rshift2, 1, 0,0) \ @@ -180,7 +180,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 +222,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/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 fd8ea7e7aa7..f14577c7d1f 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; } 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=92492
Your paranoid android.
=== w8adm (testbot log) ===
WineRunTask.pl:error: The previous 1 run(s) terminated abnormally
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== 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
On 6/15/21 5:07 PM, Paul Gofman wrote:
Signed-off-by: Paul Gofman pgofman@codeweavers.com
v3: - initialize new scope's locals tree in visit_statement( STAT_WITH case): fixes assertion in test from patch 6.
dlls/jscript/compile.c | 235 +++++++++++++++++++++++++++---------- dlls/jscript/engine.c | 105 ++++++++++++++--- dlls/jscript/engine.h | 5 +- dlls/jscript/parser.h | 2 + dlls/jscript/parser.y | 2 + dlls/jscript/tests/lang.js | 16 +++ dlls/mshtml/tests/es5.js | 48 +++++++- 7 files changed, 334 insertions(+), 79 deletions(-)
It would be nice to split it a bit. For example, it seems to me that visiting phrase changes could be a separated patch.
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 46efd53cdb5..9ac3d221ff0 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;
@@ -48,6 +50,8 @@ typedef struct { int ref; } function_local_t;
+#define MAX_SCOPE_COUNT 1024
Such arbitrary limit seems bad. Scripts can be really long, so it's not unrealistic to hit the limit. At the same time, it's wasteful for short scripts like "runOnload()".
typedef struct _compiler_ctx_t { parser_ctx_t *parser; bytecode_t *code; @@ -61,8 +65,14 @@ typedef struct _compiler_ctx_t { unsigned labels_size; unsigned labels_cnt;
- struct wine_rb_tree locals;
- unsigned locals_cnt;
struct
{
struct wine_rb_tree locals;
unsigned int locals_cnt;
unsigned int *ref_index;
}
*local_scopes;
unsigned local_scope_count;
statement_ctx_t *stat_ctx; function_code_t *func;
@@ -253,6 +263,19 @@ static HRESULT push_instr_int(compiler_ctx_t *ctx, jsop_t op, LONG arg) return S_OK; }
+static HRESULT push_instr_uint_uint(compiler_ctx_t *ctx, jsop_t op, unsigned arg1, unsigned arg2) +{
- unsigned instr;
- instr = push_instr(ctx, op);
- if(!instr)
return E_OUTOFMEMORY;
- instr_ptr(ctx, instr)->u.arg[0].uint = arg1;
- instr_ptr(ctx, instr)->u.arg[1].uint = arg2;
- return S_OK;
+}
- static HRESULT push_instr_str(compiler_ctx_t *ctx, jsop_t op, jsstr_t *str) { unsigned instr;
@@ -439,10 +462,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;
TRACE("iter->scope_index %d, ctx->func->local_scope_count %d.\n", iter->scope_index, ctx->func->local_scope_count);
Is it a debugging leftover? I wouldn't mind adding some debug traces, but this one seems kind of random.
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;
@@ -1111,18 +1143,35 @@ 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(!push_instr(ctx, OP_new_obj))
return E_OUTOFMEMORY;
if(FAILED(hres = push_instr_uint_uint(ctx, OP_push_scope, block->scope_index, TRUE)))
return hres;
Creating a new object on each block entry is a rather expensive. Could we create those object on demand instead (like in detach_variable_object())? Given that, it seems to me that a new dedicated opcode would appropriate here.
Thanks,
Jacek
On 6/16/21 22:35, Jacek Caban wrote:
It would be nice to split it a bit. For example, it seems to me that visiting phrase changes could be a separated patch.
Well, I even had most of the visiting part as a separate patch initially but squashed all that after some consideration. I will pull it back.
+#define MAX_SCOPE_COUNT 1024
Such arbitrary limit seems bad. Scripts can be really long, so it's not unrealistic to hit the limit. At the same time, it's wasteful for short scripts like "runOnload()".
I will switch that to dynamic reallocation.
+ TRACE("iter->scope_index %d, ctx->func->local_scope_count %d.\n", iter->scope_index, ctx->func->local_scope_count);
Is it a debugging leftover? I wouldn't mind adding some debug traces, but this one seems kind of random.
It is not exactly a leftover but yes, it is not particularly useful. I will remove it.
{
+ statement_ctx_t stat_ctx = {0, TRUE}; + BOOL needs_scope; HRESULT hres; + needs_scope = block && block->scope_index; + if (needs_scope) + { + if(!push_instr(ctx, OP_new_obj)) + return E_OUTOFMEMORY; + if(FAILED(hres = push_instr_uint_uint(ctx, OP_push_scope, block->scope_index, TRUE))) + return hres;
Creating a new object on each block entry is a rather expensive. Could we create those object on demand instead (like in detach_variable_object())? Given that, it seems to me that a new dedicated opcode would appropriate here.
I should note that it is not for each block but only for blocks which has any 'let' variables or functions defined in it. But it won't hurt to optimize for the case when there are just local variables and the scope ends up not being referenced on leave. I will add the opcode and create the object on detach.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v2: - detach variable object when leaving scope which is still reference (fixes test by Jacek from patch 5); - add more tests for binding function defined and referenced in various scopes; - allocate function variable in the scope where the function is defined; - keep detached function variable both in local scope object and function base scope object. v3: - put the outer scope functions to variable_obj first in detach_variable_object(): fixes some test failures in patch 6; - remove unrelated tests from "declaration_let" test.
dlls/jscript/compile.c | 39 +++++++++- dlls/jscript/engine.c | 154 +++++++++++++++++++++++++++------------ dlls/jscript/engine.h | 2 + dlls/jscript/parser.h | 1 + dlls/jscript/parser.y | 1 + dlls/mshtml/tests/es5.js | 49 +++++++++++++ 6 files changed, 195 insertions(+), 51 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 9ac3d221ff0..dd970ef8678 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;
@@ -81,6 +82,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; @@ -950,6 +952,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; }
@@ -1936,15 +1950,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) @@ -2137,7 +2163,7 @@ static HRESULT visit_block_statement(compiler_ctx_t *ctx, block_statement_t *blo if (!needs_scope) return S_OK;
- if (ctx->local_scopes[stat_ctx.scope_index].locals_cnt) + if (ctx->local_scopes[stat_ctx.scope_index].locals_cnt || stat_ctx.scope_has_functions) { block->scope_index = stat_ctx.scope_index; } @@ -2437,6 +2463,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; @@ -2520,6 +2547,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)) @@ -2541,10 +2569,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 877d67afb31..13beaf5acd3 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -565,14 +565,45 @@ HRESULT jsval_strict_equal(jsval_t lval, jsval_t rval, BOOL *ret) return S_OK; }
+static HRESULT detach_scope_chain(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t *scope) +{ + unsigned int i, index; + HRESULT hres; + + if (scope == frame->base_scope) + return S_OK; + + if (FAILED(hres = detach_scope_chain(ctx, frame, scope->next))) + return hres; + + if (!scope->frame) + return S_OK; + + assert(scope->frame == frame); + scope->frame = NULL; + 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; + if (frame->function->variables[ref].func_id != -1 + && FAILED(hres = jsdisp_propput_name(frame->variable_obj, name, ctx->stack[local_off(frame, ref)]))) + return hres; + } + 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 int i, index; - scope_chain_t *scope; + unsigned int i; HRESULT hres;
if(!frame->base_scope) @@ -580,47 +611,30 @@ static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BO
TRACE("detaching scope chain %p, frame %p.\n", ctx->call_ctx->scope, frame);
- for (scope = ctx->call_ctx->scope; scope != frame->base_scope; scope = scope->next) + if(frame->base_scope->frame) { - if (!scope->frame) - continue; + TRACE("detaching %p\n", frame);
- assert(scope->frame == frame); - scope->frame = NULL; + assert(frame == frame->base_scope->frame); + assert(frame->variable_obj == frame->base_scope->jsobj);
- index = scope->scope_index; - - for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++) - { - hres = jsdisp_propput_name(scope->jsobj, frame->function->local_scopes[index].locals[i].name, - ctx->stack[local_off(frame, frame->function->local_scopes[index].locals[i].ref)]); + if(!from_release && !frame->arguments_obj) { + hres = setup_arguments_object(ctx, frame); if(FAILED(hres)) return hres; } - }
- if(!frame->base_scope->frame) - return S_OK; + frame->base_scope->frame = NULL;
- TRACE("detaching %p\n", frame); - - assert(frame == frame->base_scope->frame); - assert(frame->variable_obj == frame->base_scope->jsobj); - - if(!from_release && !frame->arguments_obj) { - hres = setup_arguments_object(ctx, frame); - if(FAILED(hres)) - return hres; + 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; + } }
- 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; - } + detach_scope_chain(ctx, frame, ctx->call_ctx->scope); return S_OK; }
@@ -863,6 +877,7 @@ static HRESULT interp_push_scope(script_ctx_t *ctx) unsigned int scope_index = get_op_uint(ctx, 0); BOOL scope_block = get_op_uint(ctx, 1); call_frame_t *frame = ctx->call_ctx; + BOOL detached_vars; unsigned int off; jsdisp_t *dispex; IDispatch *disp; @@ -882,32 +897,57 @@ static HRESULT interp_push_scope(script_ctx_t *ctx) hres = scope_push(ctx->call_ctx->scope, dispex, disp, scope_index, &ctx->call_ctx->scope); IDispatch_Release(disp);
- if (FAILED(hres) || !scope_block) + if (FAILED(hres) || !scope_index) return hres;
- assert(dispex); + detached_vars = !(frame->base_scope && frame->base_scope->frame);
- if (frame->base_scope && frame->base_scope->frame) + if (scope_block && !detached_vars) { + assert(dispex); assert(frame->base_scope->frame == frame); frame->scope->frame = ctx->call_ctx; + }
- for(i = 0; i < frame->function->local_scopes[scope_index].locals_cnt; i++) + for(i = 0; i < frame->function->local_scopes[scope_index].locals_cnt; i++) + { + WCHAR *name = frame->function->local_scopes[scope_index].locals[i].name; + int ref = frame->function->local_scopes[scope_index].locals[i].ref; + jsdisp_t *func_obj; + jsval_t val; + + if (frame->function->variables[ref].func_id != -1) { - off = local_off(frame, frame->function->local_scopes[scope_index].locals[i].ref); - jsval_release(ctx->stack[off]); - ctx->stack[off] = jsval_undefined(); + 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) + { + hres = jsdisp_propput_name(frame->variable_obj, name, jsval_obj(func_obj)); + if(FAILED(hres)) + return hres; + } } - } - else - { - for(i = 0; i < frame->function->local_scopes[scope_index].locals_cnt; i++) + else { - hres = jsdisp_propput_name(dispex, frame->function->local_scopes[scope_index].locals[i].name, - jsval_undefined()); + val = jsval_undefined(); + } + + if (detached_vars) + { + hres = jsdisp_propput_name(dispex, name, val); if(FAILED(hres)) return hres; } + else + { + off = local_off(frame, ref); + jsval_release(ctx->stack[off]); + ctx->stack[off] = val; + } }
return S_OK; @@ -918,6 +958,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; } @@ -3112,7 +3158,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;
@@ -3166,6 +3214,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; @@ -3196,6 +3250,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 3656b32dd8d..c395ad502ff 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -176,6 +176,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 f14577c7d1f..b74b503e858 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=92493
Your paranoid android.
=== 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: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_ar (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w10pro64_zh_CN (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
Based on a patch by Jacek Caban.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v2: - added patch.
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=92494
Your paranoid android.
=== 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: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
=== w10pro64_zh_CN (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
Patch by Jacek Caban.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v3: - added patch.
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=92495
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_zh_CN (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
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=92490
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