| function truth() { |
| return true; |
| } |
| noInline(truth); |
| |
| function assert(cond) { |
| if (!cond) |
| throw new Error("broke assertion"); |
| } |
| noInline(assert); |
| |
| function shouldThrowTDZ(func) { |
| var hasThrown = false; |
| try { |
| func(); |
| } catch(e) { |
| if (e.name.indexOf("ReferenceError") !== -1) |
| hasThrown = true; |
| } |
| assert(hasThrown); |
| } |
| noInline(shouldThrowTDZ); |
| |
| |
| // Tests |
| |
| |
| const NUM_LOOPS = 1000; |
| |
| ;(function() { |
| function foo() { |
| delete x; |
| const x = 20; |
| } |
| function bar() { |
| delete x; |
| const x = 20; |
| function capX() { return x; } |
| } |
| |
| for (var i = 0; i < NUM_LOOPS; i++) { |
| shouldThrowTDZ(foo); |
| shouldThrowTDZ(bar); |
| } |
| |
| })(); |
| |
| |
| ;(function() { |
| |
| function foo() { |
| var hadError = false; |
| try { |
| x; |
| } catch(e) { |
| hadError = true; |
| } |
| assert(hadError); |
| |
| if (truth()) { |
| // This eval is enterpreted as follows: |
| // eval("var x; x = 20"); |
| // We first assign undefined to the "var x". |
| // Then, we interperet an assignment expression |
| // into the resolved variable x. x resolves to the lexical "const x;" |
| // Look at ECMA section 13.3.2.4 of the ES6 spec: |
| // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-variable-statement-runtime-semantics-evaluation |
| // And also look at section 8.3.1 ResolveBinding: |
| // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-resolvebinding |
| const x = 40; |
| let threw = false; |
| try { |
| eval("var x = 20;"); |
| } catch(e) { |
| if (e.name.indexOf("TypeError") !== -1 && e.message.indexOf("readonly") !== -1) |
| threw = true; |
| } |
| assert(threw); |
| assert(x === 40); |
| } |
| assert(x === undefined); |
| } |
| |
| function bar() { |
| var hadError = false; |
| try { |
| x; |
| } catch(e) { |
| hadError = true; |
| } |
| assert(hadError); |
| |
| if (truth()) { |
| const x = 40; |
| function capX() { return x; } |
| let threw = false; |
| try { |
| eval("var x = 20;"); |
| } catch(e) { |
| if (e.name.indexOf("TypeError") !== -1 && e.message.indexOf("readonly") !== -1) |
| threw = true; |
| } |
| assert(threw); |
| assert(x === 40); |
| } |
| assert(x === undefined); |
| } |
| |
| function baz() { |
| if (truth()) { |
| const x = 40; |
| eval("const x = 20; assert(x === 20);"); |
| assert(x === 40); |
| } |
| if (truth()) { |
| const x = 40; |
| function capX() { return x; } |
| eval("const x = 20; assert(x === 20);"); |
| assert(x === 40); |
| } |
| } |
| |
| function baz() { |
| // Test eval() caching. |
| const x = 20; |
| const evalString = "x;"; |
| |
| assert(eval(evalString) === 20); |
| if (truth()) { |
| const y = 60; |
| assert(eval(evalString) === 20); |
| assert(y === 60); |
| if (truth()) { |
| const y = 50, z = 70, x = 40; |
| assert(eval(evalString) === 40); |
| assert(y === 50 && z === 70); |
| } |
| } |
| } |
| |
| for (var i = 0; i < NUM_LOOPS; i++) { |
| foo(); |
| bar(); |
| baz(); |
| } |
| |
| })(); |