From 5c756468656afc9207c0f51f774bbc29267e1469 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Jun 2021 20:48:20 +0300 Subject: [PATCH] jscript: Implement 'let' declaration in 'for' statement. Signed-off-by: Paul Gofman Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- dlls/jscript/compile.c | 84 +++++++++++++++++++++++++++++++++------- dlls/jscript/parser.h | 1 + dlls/jscript/parser.y | 34 +++++++++++++--- dlls/mshtml/tests/es5.js | 12 ++++++ 4 files changed, 113 insertions(+), 18 deletions(-) diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index f14392b166f..4672a8c31f1 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -1377,47 +1377,63 @@ static HRESULT compile_while_statement(compiler_ctx_t *ctx, while_statement_t *s return S_OK; } -/* ECMA-262 3rd Edition 12.6.3 */ +/* ECMA-262 10th Edition 13.7.4 */ static HRESULT compile_for_statement(compiler_ctx_t *ctx, for_statement_t *stat) { statement_ctx_t stat_ctx = {0, FALSE, FALSE}; + statement_ctx_t scope_stat_ctx = {0, TRUE}; unsigned expr_off; HRESULT hres; + if (stat->scope_index) + { + if(FAILED(hres = push_instr_uint(ctx, OP_push_block_scope, stat->scope_index))) + return hres; + + scope_stat_ctx.scope_index = stat->scope_index; + scope_stat_ctx.block_scope = TRUE; + push_compiler_statement_ctx(ctx, &scope_stat_ctx); + } + if(stat->variable_list) { hres = compile_variable_list(ctx, stat->variable_list); if(FAILED(hres)) - return hres; + goto done; }else if(stat->begin_expr) { hres = compile_expression(ctx, stat->begin_expr, FALSE); if(FAILED(hres)) - return hres; + goto done; } stat_ctx.break_label = alloc_label(ctx); if(!stat_ctx.break_label) - return E_OUTOFMEMORY; + { + hres = E_OUTOFMEMORY; + goto done; + } stat_ctx.continue_label = alloc_label(ctx); if(!stat_ctx.continue_label) - return E_OUTOFMEMORY; - + { + hres = E_OUTOFMEMORY; + goto done; + } expr_off = ctx->code_off; if(stat->expr) { set_compiler_loc(ctx, stat->expr_loc); hres = compile_expression(ctx, stat->expr, TRUE); if(FAILED(hres)) - return hres; + goto done; hres = push_instr_uint(ctx, OP_jmp_z, stat_ctx.break_label); if(FAILED(hres)) - return hres; + goto done; } hres = compile_statement(ctx, &stat_ctx, stat->statement); if(FAILED(hres)) - return hres; + goto done; label_set_addr(ctx, stat_ctx.continue_label); @@ -1425,15 +1441,23 @@ static HRESULT compile_for_statement(compiler_ctx_t *ctx, for_statement_t *stat) set_compiler_loc(ctx, stat->end_loc); hres = compile_expression(ctx, stat->end_expr, FALSE); if(FAILED(hres)) - return hres; + goto done; } hres = push_instr_uint(ctx, OP_jmp, expr_off); if(FAILED(hres)) - return hres; + goto done; label_set_addr(ctx, stat_ctx.break_label); - return S_OK; + hres = S_OK; +done: + if (stat->scope_index) + { + pop_compiler_statement_ctx(ctx, &scope_stat_ctx); + if(SUCCEEDED(hres) && !push_instr(ctx, OP_pop_scope)) + return E_OUTOFMEMORY; + } + return hres; } /* ECMA-262 3rd Edition 12.6.4 */ @@ -2225,27 +2249,61 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx, s break; } case STAT_FOR: { + statement_ctx_t stat_ctx_data = {0, TRUE}, *stat_ctx = NULL; for_statement_t *for_stat = (for_statement_t*)stat; if(for_stat->variable_list) + { + variable_declaration_t *var; + + for(var = for_stat->variable_list; var; var = var->next) + { + if (var->block_scope) + { + stat_ctx = &stat_ctx_data; + break; + } + } + + if (stat_ctx) + { + if (!alloc_local_scope(ctx, &for_stat->scope_index)) + { + hres = E_OUTOFMEMORY; + break; + } + stat_ctx->scope_index = for_stat->scope_index; + stat_ctx->block_scope = TRUE; + push_compiler_statement_ctx(ctx, stat_ctx); + } hres = visit_variable_list(ctx, for_stat->variable_list); + } else if(for_stat->begin_expr) hres = visit_expression(ctx, for_stat->begin_expr); if(FAILED(hres)) + { + pop_compiler_statement_ctx(ctx, stat_ctx); break; + } if(for_stat->expr) { hres = visit_expression(ctx, for_stat->expr); if(FAILED(hres)) + { + pop_compiler_statement_ctx(ctx, stat_ctx); break; + } } hres = visit_statement(ctx, NULL, for_stat->statement); if(FAILED(hres)) + { + pop_compiler_statement_ctx(ctx, stat_ctx); break; - + } if(for_stat->end_expr) hres = visit_expression(ctx, for_stat->end_expr); + pop_compiler_statement_ctx(ctx, stat_ctx); break; } case STAT_FORIN: { diff --git a/dlls/jscript/parser.h b/dlls/jscript/parser.h index 4553280517c..fde0b540d63 100644 --- a/dlls/jscript/parser.h +++ b/dlls/jscript/parser.h @@ -171,6 +171,7 @@ typedef struct { expression_t *end_expr; unsigned end_loc; statement_t *statement; + unsigned int scope_index; } for_statement_t; typedef struct { diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index 73c03731fd4..9fc7ea61dc3 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -86,7 +86,7 @@ static statement_t *new_var_statement(parser_ctx_t*,BOOL,BOOL,unsigned,variable_ 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*); -static statement_t *new_for_statement(parser_ctx_t*,unsigned,variable_list_t*,expression_t*,expression_t*,unsigned, +static statement_t *new_for_statement(parser_ctx_t*,unsigned,variable_declaration_t*,expression_t*,expression_t*,unsigned, expression_t*,unsigned,statement_t*); static statement_t *new_forin_statement(parser_ctx_t*,unsigned,variable_declaration_t*,expression_t*,expression_t*,statement_t*); static statement_t *new_continue_statement(parser_ctx_t*,unsigned,const WCHAR*); @@ -177,6 +177,7 @@ static expression_t *new_prop_and_value_expression(parser_ctx_t*,property_list_t %type Declaration %type Block %type LexicalDeclaration +%type LexicalDeclarationNoIn %type VariableStatement %type EmptyStatement %type ExpressionStatement @@ -344,6 +345,21 @@ LexicalDeclaration $$ = new_var_statement(ctx, TRUE, TRUE, @$, $2); } +/* ECMA-262 10th Edition 13.3.1, TODO: BindingList*/ +LexicalDeclarationNoIn + : kLET VariableDeclarationListNoIn semicolon_opt + { $$ = new_var_statement(ctx, TRUE, FALSE, @$, $2); } + | kCONST VariableDeclarationListNoIn semicolon_opt + { + if(ctx->script->version < SCRIPTLANGUAGEVERSION_ES5) { + WARN("const var declaration %s 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 @@ -408,7 +424,7 @@ IfStatement | kIF left_bracket Expression_err right_bracket Statement %prec LOWER_THAN_ELSE { $$ = new_if_statement(ctx, @$, $3, $5, NULL); } -/* ECMA-262 3rd Edition 12.6 */ +/* ECMA-262 10th Edition 13.7 */ IterationStatement : kDO Statement kWHILE left_bracket Expression_err right_bracket semicolon_opt { $$ = new_while_statement(ctx, @3, TRUE, $5, $2); } @@ -425,11 +441,18 @@ IterationStatement semicolon Expression_opt { if(!explicit_error(ctx, $7, ';')) YYABORT; } semicolon Expression_opt right_bracket Statement - { $$ = new_for_statement(ctx, @3, $4, NULL, $7, @7, $10, @10, $12); } + { $$ = new_for_statement(ctx, @3, $4 ? $4->head : NULL, NULL, $7, @7, $10, @10, $12); } | kFOR left_bracket LeftHandSideExpression kIN Expression_err right_bracket Statement { $$ = new_forin_statement(ctx, @$, NULL, $3, $5, $7); } | kFOR left_bracket kVAR VariableDeclarationNoIn kIN Expression_err right_bracket Statement { $$ = new_forin_statement(ctx, @$, $4, NULL, $6, $8); } + | kFOR left_bracket LexicalDeclarationNoIn + { if(!explicit_error(ctx, $3, ';')) YYABORT; } + Expression_opt + { if(!explicit_error(ctx, $5, ';')) YYABORT; } + semicolon Expression_opt right_bracket Statement + { $$ = new_for_statement(ctx, @3, ((var_statement_t *)$3)->variable_list, + NULL, $5, @5, $8, @8, $10); } /* ECMA-262 3rd Edition 12.7 */ ContinueStatement @@ -1235,7 +1258,7 @@ static statement_t *new_while_statement(parser_ctx_t *ctx, unsigned loc, BOOL do return &ret->stat; } -static statement_t *new_for_statement(parser_ctx_t *ctx, unsigned loc, variable_list_t *variable_list, expression_t *begin_expr, +static statement_t *new_for_statement(parser_ctx_t *ctx, unsigned loc, variable_declaration_t *variable_list, expression_t *begin_expr, expression_t *expr, unsigned expr_loc, expression_t *end_expr, unsigned end_loc, statement_t *statement) { for_statement_t *ret; @@ -1244,13 +1267,14 @@ static statement_t *new_for_statement(parser_ctx_t *ctx, unsigned loc, variable_ if(!ret) return NULL; - ret->variable_list = variable_list ? variable_list->head : NULL; + ret->variable_list = variable_list; ret->begin_expr = begin_expr; ret->expr = expr; ret->expr_loc = expr_loc; ret->end_expr = end_expr; ret->end_loc = end_loc; ret->statement = statement; + ret->scope_index = 0; return &ret->stat; } diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 19de053911b..a520253569c 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1330,6 +1330,18 @@ sync_test("declaration_let", function() { except = true; } ok(except, "with({w:9}) let a = 3: expected exception."); + + let for_count = 0; + for (let for_i1 = 0, for_i2 = 1; for_i1 < 3; ++for_i1, ++for_i2, ++for_count) + { + let for_i2 = 10; + + ok(for_i2 == 10, "for_i2 != 10"); + } + + ok(typeof for_i1 == 'undefined', "for_i1 is defined"); + ok(typeof for_i2 == 'undefined', "for_i2 is defined"); + ok(for_count == 3, "for_count != 3"); }); sync_test("let scope instances", function() {