[PATCH 4/4] jscript: Implement 'let' declaration in 'for' statement.

Paul Gofman pgofman at codeweavers.com
Mon Jun 21 07:11:39 CDT 2021


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 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 5c7f48fa23f..ce5d7fb2d10 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 <statement> Declaration
 %type <statement> Block
 %type <statement> LexicalDeclaration
+%type <statement> LexicalDeclarationNoIn
 %type <statement> VariableStatement
 %type <statement> EmptyStatement
 %type <statement> 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 3467698e159..a357083de52 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() {
-- 
2.31.1




More information about the wine-devel mailing list