1// Copyright 2015 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Flags: --no-legacy-const --harmony-sloppy --harmony-sloppy-let 6// Flags: --harmony-sloppy-function --harmony-destructuring-bind 7 8// Test Annex B 3.3 semantics for functions declared in blocks in sloppy mode. 9// http://www.ecma-international.org/ecma-262/6.0/#sec-block-level-function-declarations-web-legacy-compatibility-semantics 10 11(function overridingLocalFunction() { 12 var x = []; 13 assertEquals('function', typeof f); 14 function f() { 15 x.push(1); 16 } 17 f(); 18 { 19 f(); 20 function f() { 21 x.push(2); 22 } 23 f(); 24 } 25 f(); 26 { 27 f(); 28 function f() { 29 x.push(3); 30 } 31 f(); 32 } 33 f(); 34 assertArrayEquals([1, 2, 2, 2, 3, 3, 3], x); 35})(); 36 37(function newFunctionBinding() { 38 var x = []; 39 assertEquals('undefined', typeof f); 40 { 41 f(); 42 function f() { 43 x.push(2); 44 } 45 f(); 46 } 47 f(); 48 { 49 f(); 50 function f() { 51 x.push(3); 52 } 53 f(); 54 } 55 f(); 56 assertArrayEquals([2, 2, 2, 3, 3, 3], x); 57})(); 58 59(function shadowingLetDoesntBind() { 60 let f = 1; 61 assertEquals(1, f); 62 { 63 let y = 3; 64 function f() { 65 y = 2; 66 } 67 f(); 68 assertEquals(2, y); 69 } 70 assertEquals(1, f); 71})(); 72 73(function shadowingClassDoesntBind() { 74 class f { } 75 assertEquals('class f { }', f.toString()); 76 { 77 let y = 3; 78 function f() { 79 y = 2; 80 } 81 f(); 82 assertEquals(2, y); 83 } 84 assertEquals('class f { }', f.toString()); 85})(); 86 87(function shadowingConstDoesntBind() { 88 const f = 1; 89 assertEquals(1, f); 90 { 91 let y = 3; 92 function f() { 93 y = 2; 94 } 95 f(); 96 assertEquals(2, y); 97 } 98 assertEquals(1, f); 99})(); 100 101(function shadowingVarBinds() { 102 var f = 1; 103 assertEquals(1, f); 104 { 105 let y = 3; 106 function f() { 107 y = 2; 108 } 109 f(); 110 assertEquals(2, y); 111 } 112 assertEquals('function', typeof f); 113})(); 114 115(function conditional() { 116 if (true) { 117 function f() { return 1; } 118 } else { 119 function f() { return 2; } 120 } 121 assertEquals(1, f()); 122 123 if (false) { 124 function g() { return 1; } 125 } else { 126 function g() { return 2; } 127 } 128 assertEquals(2, g()); 129})(); 130 131(function skipExecution() { 132 { 133 function f() { return 1; } 134 } 135 assertEquals(1, f()); 136 { 137 function f() { return 2; } 138 } 139 assertEquals(2, f()); 140 L: { 141 assertEquals(3, f()); 142 break L; 143 function f() { return 3; } 144 } 145 assertEquals(2, f()); 146})(); 147 148// Test that shadowing arguments is fine 149(function shadowArguments(x) { 150 assertArrayEquals([1], arguments); 151 { 152 assertEquals('function', typeof arguments); 153 function arguments() {} 154 assertEquals('function', typeof arguments); 155 } 156 assertEquals('function', typeof arguments); 157})(1); 158 159// Shadow function parameter 160(function shadowParameter(x) { 161 assertEquals(1, x); 162 { 163 function x() {} 164 } 165 assertEquals('function', typeof x); 166})(1); 167 168// Shadow function parameter 169(function shadowDefaultParameter(x = 0) { 170 assertEquals(1, x); 171 { 172 function x() {} 173 } 174 // TODO(littledan): Once destructured parameters are no longer 175 // let-bound, enable this assertion. This is the core of the test. 176 // assertEquals('function', typeof x); 177})(1); 178 179(function shadowRestParameter(...x) { 180 assertArrayEquals([1], x); 181 { 182 function x() {} 183 } 184 // TODO(littledan): Once destructured parameters are no longer 185 // let-bound, enable this assertion. This is the core of the test. 186 // assertEquals('function', typeof x); 187})(1); 188 189assertThrows(function notInDefaultScope(x = y) { 190 { 191 function y() {} 192 } 193 assertEquals('function', typeof y); 194 assertEquals(x, undefined); 195}, ReferenceError); 196 197// Test that hoisting from blocks does happen in global scope 198function globalHoisted() { return 0; } 199{ 200 function globalHoisted() { return 1; } 201} 202assertEquals(1, globalHoisted()); 203 204// Also happens when not previously defined 205assertEquals(undefined, globalUndefinedHoisted); 206{ 207 function globalUndefinedHoisted() { return 1; } 208} 209assertEquals(1, globalUndefinedHoisted()); 210var globalUndefinedHoistedDescriptor = 211 Object.getOwnPropertyDescriptor(this, "globalUndefinedHoisted"); 212assertFalse(globalUndefinedHoistedDescriptor.configurable); 213assertTrue(globalUndefinedHoistedDescriptor.writable); 214assertTrue(globalUndefinedHoistedDescriptor.enumerable); 215assertEquals(1, globalUndefinedHoistedDescriptor.value()); 216 217// When a function property is hoisted, it should be 218// made enumerable. 219// BUG(v8:4451) 220Object.defineProperty(this, "globalNonEnumerable", { 221 value: false, 222 configurable: true, 223 writable: true, 224 enumerable: false 225}); 226eval("{function globalNonEnumerable() { return 1; }}"); 227var globalNonEnumerableDescriptor 228 = Object.getOwnPropertyDescriptor(this, "globalNonEnumerable"); 229// BUG(v8:4451): Should be made non-configurable 230assertTrue(globalNonEnumerableDescriptor.configurable); 231assertTrue(globalNonEnumerableDescriptor.writable); 232// BUG(v8:4451): Should be made enumerable 233assertFalse(globalNonEnumerableDescriptor.enumerable); 234assertEquals(1, globalNonEnumerableDescriptor.value()); 235 236// When a function property is hoisted, it should be overwritten and 237// made writable and overwritten, even if the property was non-writable. 238Object.defineProperty(this, "globalNonWritable", { 239 value: false, 240 configurable: true, 241 writable: false, 242 enumerable: true 243}); 244eval("{function globalNonWritable() { return 1; }}"); 245var globalNonWritableDescriptor 246 = Object.getOwnPropertyDescriptor(this, "globalNonWritable"); 247// BUG(v8:4451): Should be made non-configurable 248assertTrue(globalNonWritableDescriptor.configurable); 249// BUG(v8:4451): Should be made writable 250assertFalse(globalNonWritableDescriptor.writable); 251assertFalse(globalNonEnumerableDescriptor.enumerable); 252// BUG(v8:4451): Should be overwritten 253assertEquals(false, globalNonWritableDescriptor.value); 254 255// Test that hoisting from blocks does happen in an eval 256eval(` 257 function evalHoisted() { return 0; } 258 { 259 function evalHoisted() { return 1; } 260 } 261 assertEquals(1, evalHoisted()); 262`); 263 264// Test that hoisting from blocks happens from eval in a function 265!function() { 266 eval(` 267 function evalInFunctionHoisted() { return 0; } 268 { 269 function evalInFunctionHoisted() { return 1; } 270 } 271 assertEquals(1, evalInFunctionHoisted()); 272 `); 273}(); 274 275let dontHoistGlobal; 276{ function dontHoistGlobal() {} } 277assertEquals(undefined, dontHoistGlobal); 278 279let dontHoistEval; 280// BUG(v8:) This shouldn't hoist and shouldn't throw 281var throws = false; 282try { 283 eval("{ function dontHoistEval() {} }"); 284} catch (e) { 285 throws = true; 286} 287assertTrue(throws); 288 289// When the global object is frozen, silently don't hoist 290// Currently this actually throws BUG(v8:4452) 291Object.freeze(this); 292throws = false; 293try { 294 eval('{ function hoistWhenFrozen() {} }'); 295} catch (e) { 296 throws = true; 297} 298assertFalse(this.hasOwnProperty("hoistWhenFrozen")); 299assertThrows(() => hoistWhenFrozen, ReferenceError); 300// Should be assertFalse BUG(v8:4452) 301assertTrue(throws); 302