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