jscript: Parse 'let' and 'const' variable declarations.

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-15 18:07:34 +03:00 committed by Alexandre Julliard
parent 6f57297c4a
commit b356962997
6 changed files with 91 additions and 5 deletions

View file

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

View file

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

View file

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

View file

@ -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 %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
{ $$ = 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;
}

View file

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

View file

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