Signed-off-by: Paul Gofman <pgofman(a)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");
+});
--
2.31.1