Source/JavaScriptCore/ChangeLog

 12017-07-04 Oleksandr Skachkov <gskachkov@gmail.com>
 2
 3 [JSC] Throw exception in case of redeclaration let variable in eval
 4 https://bugs.webkit.org/show_bug.cgi?id=167837
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Current patch allow to prevent the redeclaration of let/const variable
 9 by var variable within eval. Before this patch error of redeclaration raised
 10 outside of eval or within eval but if top level function declared i.e:
 11 ```
 12 {
 13 let foo;
 14 eval('function foo(){}');
 15 }
 16 ```
 17 Current patch also cover case for var variable:
 18 ```
 19 {
 20 let foo;
 21 eval('var foo');
 22 }
 23 ```
 24 and raises early syntax error. This change is addressed to following part of the spec:
 25 https://tc39.github.io/ecma262/#sec-evaldeclarationinstantiation p 5.d.ii.2.a.i
 26 https://tc39.github.io/ecma262/#sec-functiondeclarationinstantiation p.9.2.12.29
 27 Current patch also allow to override the variable in catch block
 28 https://bugs.webkit.org/show_bug.cgi?id=168184
 29 ```
 30 (function boo() {
 31 try {
 32 throw new Error('error');
 33 } catch(e) {
 34 assert(e instanceof Error);
 35 eval('var e = 10;');
 36 assert(e === 10);
 37 }
 38 assert(typeof e === 'undefined');
 39 })();
 40 ```
 41
 42 * interpreter/Interpreter.cpp:
 43 (JSC::Interpreter::execute):
 44 * runtime/JSScope.cpp:
 45 (JSC::JSScope::resolveScopeForHoistingFuncDeclInEval):
 46
1472017-07-04 Joseph Pecoraro <pecoraro@apple.com>
248
349 Cleanup some StringBuilder use

Source/JavaScriptCore/interpreter/Interpreter.cpp

@@JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue
11791179
11801180 for (unsigned i = 0; i < numVariables; ++i) {
11811181 const Identifier& ident = codeBlock->variable(i);
 1182 if (!eval->isStrictMode()) {
 1183 JSValue resolvedScope = JSScope::resolveScopeForHoistingFuncDeclInEval(callFrame, scope, ident);
 1184 if (resolvedScope.isUndefined())
 1185 return checkedReturn(throwSyntaxError(callFrame, throwScope, makeString("Cannot declare a var variable that shadows a let/const/class variable: '", String(ident.impl()), "'")));
 1186 }
11821187 bool hasProperty = variableObject->hasProperty(callFrame, ident);
11831188 RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception()));
11841189 if (!hasProperty) {

Source/JavaScriptCore/runtime/JSScope.cpp

@@ALWAYS_INLINE JSObject* JSScope::resolve(ExecState* exec, JSScope* scope, const
259259JSValue JSScope::resolveScopeForHoistingFuncDeclInEval(ExecState* exec, JSScope* scope, const Identifier& ident)
260260{
261261 auto returnPredicate = [&] (JSScope* scope) -> bool {
262  return scope->isVarScope();
 262 return scope->isVarScope() || scope->isCatchScope();
263263 };
264264 auto skipPredicate = [&] (JSScope* scope) -> bool {
265265 return scope->isWithScope();

@@JSValue JSScope::resolveScopeForHoistingFuncDeclInEval(ExecState* exec, JSScope*
269269 bool result = false;
270270 if (JSScope* scope = jsDynamicCast<JSScope*>(exec->vm(), object)) {
271271 if (SymbolTable* scopeSymbolTable = scope->symbolTable(exec->vm())) {
272  result = scope->isGlobalObject()
273  ? JSObject::isExtensible(object, exec)
274  : scopeSymbolTable->scopeType() == SymbolTable::ScopeType::VarScope;
 272 result = scope->isGlobalObject() || scopeSymbolTable->scopeType() == SymbolTable::ScopeType::VarScope || scopeSymbolTable->scopeType() == SymbolTable::ScopeType::CatchScope;
275273 }
276274 }
277275

JSTests/ChakraCore/test/Closures/bug_OS_2299723.baseline-jsc

 1eval('var x = 5') threw 'Cannot declare a var variable that shadows a let/const/class variable: 'x''
12x: 5
2 eval('var y = 5') threw 'Attempted to assign to readonly property.'
 3eval('var y = 5') threw 'Cannot declare a var variable that shadows a let/const/class variable: 'y''
34eval('y = 5') threw 'Attempted to assign to readonly property.'
45y: 1

JSTests/ChangeLog

 12017-07-04 Oleksandr Skachkov <gskachkov@gmail.com>
 2
 3 [JSC] Throw exception in case of redeclaration let variable in eval
 4 https://bugs.webkit.org/show_bug.cgi?id=167837
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * ChakraCore/test/Closures/bug_OS_2299723.baseline-jsc:
 9 * stress/const-not-strict-mode.js:
 10 (foo):
 11 * stress/eval-func-decl-in-global-of-eval.js:
 12 * stress/eval-func-decl-with-let-const-class.js:
 13 (barWithTry):
 14 (foobarWithTry):
 15 (bazWithTry):
 16 * stress/eval-func-decl-within-eval-with-reassign-to-var.js:
 17 * stress/eval-let-const-redeclararion.js: Added.
 18 (assert):
 19 (catch):
 20 (try.foo):
 21 (boo):
 22 (try.with):
 23 (with):
 24 * stress/lexical-let-not-strict-mode.js:
 25 (foo):
 26 * test262.yaml:
 27
1282017-07-03 Saam Barati <sbarati@apple.com>
229
330 Skip unshiftCountSlowCase-correct-postCapacity.js on debug builds since it takes a long time to run.

JSTests/stress/const-not-strict-mode.js

@@function foo() {
7272 try {
7373 eval("var x = 20;");
7474 } catch(e) {
75  if (e.name.indexOf("TypeError") !== -1 && e.message.indexOf("readonly") !== -1)
76  threw = true;
 75 threw = e instanceof SyntaxError && e.message.indexOf('shadows') !== -1;
7776 }
7877 assert(threw);
7978 assert(x === 40);
8079 }
81  assert(x === undefined);
 80 assert(typeof x === 'undefined');
8281}
8382
8483function bar() {

@@function bar() {
9796 try {
9897 eval("var x = 20;");
9998 } catch(e) {
100  if (e.name.indexOf("TypeError") !== -1 && e.message.indexOf("readonly") !== -1)
101  threw = true;
 99 threw = e instanceof SyntaxError && e.message.indexOf('shadows') !== -1;
102100 }
103101 assert(threw);
104102 assert(x === 40);
105103 }
106  assert(x === undefined);
 104 assert(typeof x === 'undefined');
107105}
108106
109107function baz() {

JSTests/stress/eval-func-decl-in-global-of-eval.js

@@function bar() {
3030
3131
3232for (var i = 0; i < 10000; i++){
33  assertThrow(() => bar(), "SyntaxError: Can't create duplicate variable in eval: 'f'");
 33 assertThrow(() => bar(), "SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'f'");
3434 assertThrow(() => f, "ReferenceError: Can't find variable: f");
3535}
3636

JSTests/stress/eval-func-decl-with-let-const-class.js

@@function barWithTry () {
119119 }
120120 assert(typeof f, 'undefined');
121121 }
122  assert(error.toString(), 'SyntaxError: Can\'t create duplicate variable in eval: \'f\'');
 122 assert(error.toString(), 'SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: \'f\'');
123123}
124124
125125function foobarWithTry () {

@@function foobarWithTry () {
133133 }
134134 assert(typeof f, 'undefined');
135135 }
136  assert(error.toString(), 'SyntaxError: Can\'t create duplicate variable in eval: \'f\'');
 136 assert(error.toString(), 'SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: \'f\'');
137137}
138138
139139function bazWithTry () {

@@function bazWithTry () {
147147 }
148148 assert(typeof f, 'undefined');
149149 }
150  assert(error.toString(), 'SyntaxError: Can\'t create duplicate variable in eval: \'f\'');
 150 assert(error.toString(), 'SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: \'f\'');
151151}
152152
153153assertNotThrow(() => boo());
154 assertThrow(() => bar(), 'SyntaxError: Can\'t create duplicate variable in eval: \'f\'');
155 assertThrow(() => foobar(), 'SyntaxError: Can\'t create duplicate variable in eval: \'f\'');
156 assertThrow(() => baz(), 'SyntaxError: Can\'t create duplicate variable in eval: \'f\'');
 154assertThrow(() => bar(), 'SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: \'f\'');
 155assertThrow(() => foobar(), 'SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: \'f\'');
 156assertThrow(() => baz(), 'SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: \'f\'');
157157
158158barWithTry();
159159foobarWithTry();

JSTests/stress/eval-func-decl-within-eval-with-reassign-to-var.js

@@for (var i = 0; i < 10000; i++){
6060// Fixme: https://bugs.webkit.org/show_bug.cgi?id=167837
6161// Current test does not work because it should raise exception
6262// that f could not be redeclared
63 /*
 63
6464function goo() {
6565 {
6666 var error = false;

@@for (var i = 0; i < 10000; i++) {
7979 goo();
8080 assert(typeof f, "undefined", "#7");
8181}
82 */
83 
84 function hoo() {
85  {
86  let h = 20;
87  eval('var h = 15; eval(" if (false){ function h() { }; } ");');
88  assert(h, 15);
89  }
90  assert(typeof h, "undefined");
91 }
92 
93 for (var i = 0; i < 10000; i++) {
94  hoo();
95  assertThrow(() => h, "ReferenceError: Can't find variable: h");
96 }
9782
9883function joo() {
9984 {

JSTests/stress/eval-let-const-redeclararion.js

 1var noError = false;
 2
 3function assert(cond) {
 4 if (!cond)
 5 throw new Error("broke assertion");
 6}
 7
 8try {
 9 (function () { let a; eval('var a'); })();
 10} catch (e) {
 11 noError = e instanceof SyntaxError;
 12}
 13
 14assert(noError, 'Expected syntax error in case var tried shadow let/const');
 15
 16
 17var noError = false;
 18
 19try {
 20 (function () { let a; eval('{ var a = 10; }'); })();
 21} catch (e) {
 22 noError = e instanceof SyntaxError;
 23}
 24
 25assert(noError, 'Expected syntax error in case var tried shadow let/const');
 26
 27var noError = false;
 28
 29try {
 30 function foo() { let a; eval('{ var a = 10; }'); }
 31 foo();
 32} catch (e) {
 33 noError = e instanceof SyntaxError;
 34}
 35
 36assert(noError, 'Expected syntax error in case var tried shadow let/const');
 37
 38try {
 39 (function () { const a = ''; eval('var a'); })();
 40} catch (e) {
 41 noError = e instanceof SyntaxError;
 42}
 43
 44assert(noError, 'Expected syntax error in case var tried shadow let/const');
 45
 46
 47var noError = false;
 48
 49try {
 50 (function () { const a = ''; eval('{ var a = 10; }'); })();
 51} catch (e) {
 52 noError = e instanceof SyntaxError;
 53}
 54
 55assert(noError, 'Expected syntax error in case var tried shadow let/const');
 56
 57
 58var noError = false;
 59
 60try {
 61 function foo() { const a = ''; eval('{ var a = 10; }'); }
 62 foo();
 63} catch (e) {
 64 noError = e instanceof SyntaxError;
 65}
 66
 67assert(noError, 'Expected syntax error in case var tried shadow let/const');
 68
 69noError = false;
 70
 71function boo() {
 72 try {
 73 throw new Error('error');
 74 } catch(e) {
 75 noError = e instanceof Error;
 76 eval('var e = 10;');
 77 noError = noError && e === 10;
 78 }
 79 noError = noError && typeof e === 'undefined';
 80};
 81boo();
 82
 83assert(noError, 'Expected that var in eval can override variable in catch block');
 84
 85noError = false;
 86
 87function foo() {
 88 try {
 89 throw new Error('error');
 90 } catch(e) {
 91 noError = e instanceof Error;
 92 eval('function e() { return "abcd"; }');
 93 noError = noError && typeof e === 'function' && e() === 'abcd';
 94 }
 95 noError = noError && typeof e === 'undefined';
 96};
 97foo();
 98
 99assert(noError, 'Expected that function in eval can override variable in catch block');
 100
 101
 102noError = false;
 103
 104try {
 105 (function () {
 106 var o = {a:'1'};
 107 let a;
 108 with (o) {
 109 eval('var a');
 110 }
 111 })();
 112} catch (error) {
 113 noError = error instanceof SyntaxError;
 114}
 115
 116assert(noError, 'Expected `with` is not affect early SyntaxError in eval');
 117
 118noError = false;
 119
 120(function () {
 121 var o = { a:'1' };
 122 with (o) {
 123 eval('var a = 10;');
 124 }
 125 noError = a === undefined && o.a === 10;
 126})();
 127
 128assert(noError, 'Expected `with` is not affect early SyntaxError in eval');
 129
 130noError = false;
 131
 132{
 133 let a = 10;
 134 noError = a === 10;
 135 (function () {
 136 noError = noError && a === 10;
 137 eval('var a = 20');
 138 noError = noError && a === 20;
 139 })();
 140 noError = noError && a === 10;
 141}
 142
 143assert(noError, 'Expected `with` is not affect early SyntaxError in eval');
 144
 145{
 146 const a = 10;
 147 noError = a === 10;
 148 (function () {
 149 noError = noError && a === 10;
 150 eval('var a = 20');
 151 noError = noError && a === 20;
 152 })();
 153 noError = noError && a === 10;
 154}
 155
 156assert(noError, 'Expected `with` is not affect early SyntaxError in eval');

JSTests/stress/lexical-let-not-strict-mode.js

@@function foo() {
6363 // And also look at section 8.3.1 ResolveBinding:
6464 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-resolvebinding
6565 let x = 40;
66  eval("var x = 20;");
67  assert(x === 20);
 66 hadError = false;
 67 try {
 68 eval("var x = 20;");
 69 } catch (e) {
 70 hadError = e instanceof SyntaxError && e.message.indexOf('let/const') !== -1;
 71 }
 72 assert(x === 40 && hadError);
6873 }
69  assert(x === undefined);
 74 assert(typeof x === 'undefined' && hadError);
7075}
7176
7277function bar() {

@@function bar() {
7883 }
7984 assert(hadError);
8085
 86 hadError = false
8187 if (truth()) {
8288 let x = 40;
8389 function capX() { return x; }
84  eval("var x = 20;");
85  assert(x === 20);
 90 try {
 91 eval("var x = 20;");
 92 } catch (e) {
 93 hadError = e instanceof SyntaxError && e.message.indexOf('let/const') !== -1;
 94 }
 95 assert(x === 40 && hadError);
8696 }
87  assert(x === undefined);
 97 assert(typeof x === 'undefined' && hadError);
8898}
8999
90100function baz() {

JSTests/test262.yaml

15001500- path: test262/test/annexB/language/eval-code/direct/global-switch-dflt-eval-global-update.js
15011501 cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
15021502- path: test262/test/annexB/language/eval-code/direct/var-env-lower-lex-catch-non-strict.js
1503  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
 1503 cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
15041504- path: test262/test/annexB/language/eval-code/indirect/global-block-decl-eval-global-block-scoping.js
15051505 cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
15061506- path: test262/test/annexB/language/eval-code/indirect/global-block-decl-eval-global-exsting-block-fn-no-init.js

5269252692- path: test262/test/language/eval-code/direct/var-env-lower-lex-catch-non-strict.js
5269352693 cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
5269452694- path: test262/test/language/eval-code/direct/var-env-lower-lex-non-strict.js
52695  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 52695 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
5269652696- path: test262/test/language/eval-code/direct/var-env-lower-lex-strict-caller.js
5269752697 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
5269852698- path: test262/test/language/eval-code/direct/var-env-lower-lex-strict-source.js

5410054100- path: test262/test/language/expressions/arrow-function/prototype-rules.js
5410154101 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
5410254102- path: test262/test/language/expressions/arrow-function/scope-body-lex-distinct.js
54103  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 54103 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
5410454104- path: test262/test/language/expressions/arrow-function/scope-param-elem-var-close.js
5410554105 cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
5410654106- path: test262/test/language/expressions/arrow-function/scope-param-elem-var-open.js

6221262212- path: test262/test/language/expressions/function/params-trailing-comma.js
6221362213 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
6221462214- path: test262/test/language/expressions/function/scope-body-lex-distinct.js
62215  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 62215 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6221662216- path: test262/test/language/expressions/function/scope-name-var-close.js
6221762217 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6221862218- path: test262/test/language/expressions/function/scope-name-var-close.js

6306263062- path: test262/test/language/expressions/generators/return.js
6306363063 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
6306463064- path: test262/test/language/expressions/generators/scope-body-lex-distinct.js
63065  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 63065 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6306663066- path: test262/test/language/expressions/generators/scope-name-var-close.js
6306763067 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6306863068- path: test262/test/language/expressions/generators/scope-name-var-close.js

6682866828- path: test262/test/language/expressions/object/property-name-yield.js
6682966829 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
6683066830- path: test262/test/language/expressions/object/scope-gen-meth-body-lex-distinct.js
66831  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 66831 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6683266832- path: test262/test/language/expressions/object/scope-gen-meth-param-elem-var-close.js
6683366833 cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6683466834- path: test262/test/language/expressions/object/scope-gen-meth-param-elem-var-open.js

6684666846- path: test262/test/language/expressions/object/scope-gen-meth-paramsbody-var-open.js
6684766847 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
6684866848- path: test262/test/language/expressions/object/scope-getter-body-lex-distinc.js
66849  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 66849 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6685066850- path: test262/test/language/expressions/object/scope-meth-body-lex-distinct.js
66851  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 66851 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6685266852- path: test262/test/language/expressions/object/scope-meth-param-elem-var-close.js
6685366853 cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6685466854- path: test262/test/language/expressions/object/scope-meth-param-elem-var-open.js

6686666866- path: test262/test/language/expressions/object/scope-meth-paramsbody-var-open.js
6686766867 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
6686866868- path: test262/test/language/expressions/object/scope-setter-body-lex-distinc.js
66869  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 66869 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6687066870- path: test262/test/language/expressions/object/scope-setter-paramsbody-var-close.js
6687166871 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
6687266872- path: test262/test/language/expressions/object/scope-setter-paramsbody-var-close.js

8338883388- path: test262/test/language/statements/function/params-trailing-comma.js
8338983389 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
8339083390- path: test262/test/language/statements/function/scope-body-lex-distinct.js
83391  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 83391 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
8339283392- path: test262/test/language/statements/function/scope-param-elem-var-close.js
8339383393 cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
8339483394- path: test262/test/language/statements/function/scope-param-elem-var-open.js

8422684226- path: test262/test/language/statements/generators/return.js
8422784227 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
8422884228- path: test262/test/language/statements/generators/scope-body-lex-distinct.js
84229  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 84229 cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
8423084230- path: test262/test/language/statements/generators/scope-param-elem-var-close.js
8423184231 cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
8423284232- path: test262/test/language/statements/generators/scope-param-elem-var-open.js