// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Flags: --no-legacy-const --harmony-sloppy --harmony-sloppy-let // Flags: --harmony-sloppy-function --harmony-destructuring-bind // Flags: --harmony-rest-parameters // Test Annex B 3.3 semantics for functions declared in blocks in sloppy mode. // http://www.ecma-international.org/ecma-262/6.0/#sec-block-level-function-declarations-web-legacy-compatibility-semantics (function overridingLocalFunction() { var x = []; assertEquals('function', typeof f); function f() { x.push(1); } f(); { f(); function f() { x.push(2); } f(); } f(); { f(); function f() { x.push(3); } f(); } f(); assertArrayEquals([1, 2, 2, 2, 3, 3, 3], x); })(); (function newFunctionBinding() { var x = []; assertEquals('undefined', typeof f); { f(); function f() { x.push(2); } f(); } f(); { f(); function f() { x.push(3); } f(); } f(); assertArrayEquals([2, 2, 2, 3, 3, 3], x); })(); (function shadowingLetDoesntBind() { let f = 1; assertEquals(1, f); { let y = 3; function f() { y = 2; } f(); assertEquals(2, y); } assertEquals(1, f); })(); (function shadowingClassDoesntBind() { class f { } assertEquals('class f { }', f.toString()); { let y = 3; function f() { y = 2; } f(); assertEquals(2, y); } assertEquals('class f { }', f.toString()); })(); (function shadowingConstDoesntBind() { const f = 1; assertEquals(1, f); { let y = 3; function f() { y = 2; } f(); assertEquals(2, y); } assertEquals(1, f); })(); (function shadowingVarBinds() { var f = 1; assertEquals(1, f); { let y = 3; function f() { y = 2; } f(); assertEquals(2, y); } assertEquals('function', typeof f); })(); (function conditional() { if (true) { function f() { return 1; } } else { function f() { return 2; } } assertEquals(1, f()); if (false) { function g() { return 1; } } else { function g() { return 2; } } assertEquals(2, g()); })(); (function skipExecution() { { function f() { return 1; } } assertEquals(1, f()); { function f() { return 2; } } assertEquals(2, f()); L: { assertEquals(3, f()); break L; function f() { return 3; } } assertEquals(2, f()); })(); // Test that shadowing arguments is fine (function shadowArguments(x) { assertArrayEquals([1], arguments); { assertEquals('function', typeof arguments); function arguments() {} assertEquals('function', typeof arguments); } assertEquals('function', typeof arguments); })(1); // Shadow function parameter (function shadowParameter(x) { assertEquals(1, x); { function x() {} } assertEquals('function', typeof x); })(1); // Shadow function parameter (function shadowDefaultParameter(x = 0) { assertEquals(1, x); { function x() {} } // TODO(littledan): Once destructured parameters are no longer // let-bound, enable this assertion. This is the core of the test. // assertEquals('function', typeof x); })(1); (function shadowRestParameter(...x) { assertArrayEquals([1], x); { function x() {} } // TODO(littledan): Once destructured parameters are no longer // let-bound, enable this assertion. This is the core of the test. // assertEquals('function', typeof x); })(1); assertThrows(function notInDefaultScope(x = y) { { function y() {} } assertEquals('function', typeof y); assertEquals(x, undefined); }, ReferenceError); // Test that hoisting from blocks does happen in global scope function globalHoisted() { return 0; } { function globalHoisted() { return 1; } } assertEquals(1, globalHoisted()); // Also happens when not previously defined assertEquals(undefined, globalUndefinedHoisted); { function globalUndefinedHoisted() { return 1; } } assertEquals(1, globalUndefinedHoisted()); var globalUndefinedHoistedDescriptor = Object.getOwnPropertyDescriptor(this, "globalUndefinedHoisted"); assertFalse(globalUndefinedHoistedDescriptor.configurable); assertTrue(globalUndefinedHoistedDescriptor.writable); assertTrue(globalUndefinedHoistedDescriptor.enumerable); assertEquals(1, globalUndefinedHoistedDescriptor.value()); // When a function property is hoisted, it should be // made enumerable. // BUG(v8:4451) Object.defineProperty(this, "globalNonEnumerable", { value: false, configurable: true, writable: true, enumerable: false }); eval("{function globalNonEnumerable() { return 1; }}"); var globalNonEnumerableDescriptor = Object.getOwnPropertyDescriptor(this, "globalNonEnumerable"); // BUG(v8:4451): Should be made non-configurable assertTrue(globalNonEnumerableDescriptor.configurable); assertTrue(globalNonEnumerableDescriptor.writable); // BUG(v8:4451): Should be made enumerable assertFalse(globalNonEnumerableDescriptor.enumerable); assertEquals(1, globalNonEnumerableDescriptor.value()); // When a function property is hoisted, it should be overwritten and // made writable and overwritten, even if the property was non-writable. Object.defineProperty(this, "globalNonWritable", { value: false, configurable: true, writable: false, enumerable: true }); eval("{function globalNonWritable() { return 1; }}"); var globalNonWritableDescriptor = Object.getOwnPropertyDescriptor(this, "globalNonWritable"); // BUG(v8:4451): Should be made non-configurable assertTrue(globalNonWritableDescriptor.configurable); // BUG(v8:4451): Should be made writable assertFalse(globalNonWritableDescriptor.writable); assertFalse(globalNonEnumerableDescriptor.enumerable); // BUG(v8:4451): Should be overwritten assertEquals(false, globalNonWritableDescriptor.value); // Test that hoisting from blocks does happen in an eval eval(` function evalHoisted() { return 0; } { function evalHoisted() { return 1; } } assertEquals(1, evalHoisted()); `); // Test that hoisting from blocks happens from eval in a function !function() { eval(` function evalInFunctionHoisted() { return 0; } { function evalInFunctionHoisted() { return 1; } } assertEquals(1, evalInFunctionHoisted()); `); }(); let dontHoistGlobal; { function dontHoistGlobal() {} } assertEquals(undefined, dontHoistGlobal); let dontHoistEval; // BUG(v8:) This shouldn't hoist and shouldn't throw var throws = false; try { eval("{ function dontHoistEval() {} }"); } catch (e) { throws = true; } assertTrue(throws); // When the global object is frozen, silently don't hoist // Currently this actually throws BUG(v8:4452) Object.freeze(this); throws = false; try { eval('{ function hoistWhenFrozen() {} }'); } catch (e) { throws = true; } assertFalse(this.hasOwnProperty("hoistWhenFrozen")); assertThrows(() => hoistWhenFrozen, ReferenceError); // Should be assertFalse BUG(v8:4452) assertTrue(throws);