jscript: Implement 'let' declaration in 'for' statement.

Signed-off-by: Paul Gofman <pgofman@codeweavers.com>
Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Paul Gofman 2021-06-21 20:48:20 +03:00 committed by Alexandre Julliard
parent deeadb4fa6
commit 5c75646865
4 changed files with 113 additions and 18 deletions

View file

@ -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: {

View file

@ -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 {

View file

@ -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;
}

View file

@ -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() {