mirror of
				https://github.com/tc39/test262.git
				synced 2025-10-31 03:34:08 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			669 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			669 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // 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.
 | |
| 
 | |
| // 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 shadowingLetDoesntBindGenerator() {
 | |
|   let f = function *f() {
 | |
|     while(true) {
 | |
|       yield 1;
 | |
|     }
 | |
|   };
 | |
|   assertEquals(1, f().next().value);
 | |
|   {
 | |
|     function *f() {
 | |
|       while(true) {
 | |
|         yield 2;
 | |
|       }
 | |
|     }
 | |
|     assertEquals(2, f().next().value);
 | |
|   }
 | |
|   assertEquals(1, f().next().value);
 | |
| })();
 | |
| 
 | |
| (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 complexParams(a = 0) {
 | |
|   {
 | |
|     let y = 3;
 | |
|     function f(b = 0) {
 | |
|       y = 2;
 | |
|     }
 | |
|     f();
 | |
|     assertEquals(2, y);
 | |
|   }
 | |
|   assertEquals('function', typeof f);
 | |
| })();
 | |
| 
 | |
| (function complexVarParams(a = 0) {
 | |
|   var f;
 | |
|   {
 | |
|     let y = 3;
 | |
|     function f(b = 0) {
 | |
|       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());
 | |
| })();
 | |
| 
 | |
| (function executionOrder() {
 | |
|   function getOuter() {
 | |
|     return f;
 | |
|   }
 | |
|   assertEquals('undefined', typeof getOuter());
 | |
| 
 | |
|   {
 | |
|     assertEquals('function', typeof f);
 | |
|     assertEquals('undefined', typeof getOuter());
 | |
|     function f () {}
 | |
|     assertEquals('function', typeof f);
 | |
|     assertEquals('function', typeof getOuter());
 | |
|   }
 | |
| 
 | |
|   assertEquals('function', typeof getOuter());
 | |
| })();
 | |
| 
 | |
| (function reassignBindings() {
 | |
|   function getOuter() {
 | |
|     return f;
 | |
|   }
 | |
|   assertEquals('undefined', typeof getOuter());
 | |
| 
 | |
|   {
 | |
|     assertEquals('function', typeof f);
 | |
|     assertEquals('undefined', typeof getOuter());
 | |
|     f = 1;
 | |
|     assertEquals('number', typeof f);
 | |
|     assertEquals('undefined', typeof getOuter());
 | |
|     function f () {}
 | |
|     assertEquals('number', typeof f);
 | |
|     assertEquals('number', typeof getOuter());
 | |
|     f = '';
 | |
|     assertEquals('string', typeof f);
 | |
|     assertEquals('number', typeof getOuter());
 | |
|   }
 | |
| 
 | |
|   assertEquals('number', typeof getOuter());
 | |
| })();
 | |
| 
 | |
| // 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);
 | |
| 
 | |
| 
 | |
| // Don't shadow simple parameter
 | |
| (function shadowingParameterDoesntBind(x) {
 | |
|   assertEquals(1, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals(1, x);
 | |
| })(1);
 | |
| 
 | |
| // Don't shadow complex parameter
 | |
| (function shadowingDefaultParameterDoesntBind(x = 0) {
 | |
|   assertEquals(1, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals(1, x);
 | |
| })(1);
 | |
| 
 | |
| // Don't shadow nested complex parameter
 | |
| (function shadowingNestedParameterDoesntBind([[x]]) {
 | |
|   assertEquals(1, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals(1, x);
 | |
| })([[1]]);
 | |
| 
 | |
| // Don't shadow rest parameter
 | |
| (function shadowingRestParameterDoesntBind(...x) {
 | |
|   assertArrayEquals([1], x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertArrayEquals([1], x);
 | |
| })(1);
 | |
| 
 | |
| // Don't shadow complex rest parameter
 | |
| (function shadowingComplexRestParameterDoesntBind(...[x]) {
 | |
|   assertArrayEquals(1, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertArrayEquals(1, x);
 | |
| })(1);
 | |
| 
 | |
| // Previous tests with a var declaration thrown in.
 | |
| // Don't shadow simple parameter
 | |
| (function shadowingVarParameterDoesntBind(x) {
 | |
|   var x;
 | |
|   assertEquals(1, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals(1, x);
 | |
| })(1);
 | |
| 
 | |
| // Don't shadow complex parameter
 | |
| (function shadowingVarDefaultParameterDoesntBind(x = 0) {
 | |
|   var x;
 | |
|   assertEquals(1, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals(1, x);
 | |
| })(1);
 | |
| 
 | |
| // Don't shadow nested complex parameter
 | |
| (function shadowingVarNestedParameterDoesntBind([[x]]) {
 | |
|   var x;
 | |
|   assertEquals(1, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals(1, x);
 | |
| })([[1]]);
 | |
| 
 | |
| // Don't shadow rest parameter
 | |
| (function shadowingVarRestParameterDoesntBind(...x) {
 | |
|   var x;
 | |
|   assertArrayEquals([1], x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertArrayEquals([1], x);
 | |
| })(1);
 | |
| 
 | |
| // Don't shadow complex rest parameter
 | |
| (function shadowingVarComplexRestParameterDoesntBind(...[x]) {
 | |
|   var x;
 | |
|   assertArrayEquals(1, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertArrayEquals(1, x);
 | |
| })(1);
 | |
| 
 | |
| 
 | |
| // Hoisting is not affected by other simple parameters
 | |
| (function irrelevantParameterBinds(y, z) {
 | |
|   assertEquals(undefined, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals('function', typeof x);
 | |
| })(1);
 | |
| 
 | |
| // Hoisting is not affected by other complex parameters
 | |
| (function irrelevantComplexParameterBinds([y] = [], z) {
 | |
|   assertEquals(undefined, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals('function', typeof x);
 | |
| })();
 | |
| 
 | |
| // Hoisting is not affected by rest parameters
 | |
| (function irrelevantRestParameterBinds(y, ...z) {
 | |
|   assertEquals(undefined, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals('function', typeof x);
 | |
| })();
 | |
| 
 | |
| // Hoisting is not affected by complex rest parameters
 | |
| (function irrelevantRestParameterBinds(y, ...[z]) {
 | |
|   assertEquals(undefined, x);
 | |
|   {
 | |
|     function x() {}
 | |
|   }
 | |
|   assertEquals('function', typeof x);
 | |
| })();
 | |
| 
 | |
| 
 | |
| // Test that shadowing function name is fine
 | |
| {
 | |
|   let called = false;
 | |
|   (function shadowFunctionName() {
 | |
|     if (called) assertUnreachable();
 | |
|     called = true;
 | |
|     {
 | |
|       function shadowFunctionName() {
 | |
|         return 0;
 | |
|       }
 | |
|       assertEquals(0, shadowFunctionName());
 | |
|     }
 | |
|     assertEquals(0, shadowFunctionName());
 | |
|   })();
 | |
| }
 | |
| 
 | |
| {
 | |
|   let called = false;
 | |
|   (function shadowFunctionNameWithComplexParameter(...r) {
 | |
|     if (called) assertUnreachable();
 | |
|     called = true;
 | |
|     {
 | |
|       function shadowFunctionNameWithComplexParameter() {
 | |
|         return 0;
 | |
|       }
 | |
|       assertEquals(0, shadowFunctionNameWithComplexParameter());
 | |
|     }
 | |
|     assertEquals(0, shadowFunctionNameWithComplexParameter());
 | |
|   })();
 | |
| }
 | |
| 
 | |
| (function shadowOuterVariable() {
 | |
|   {
 | |
|     let f = 0;
 | |
|     (function () {
 | |
|       assertEquals(undefined, f);
 | |
|       {
 | |
|         assertEquals(1, f());
 | |
|         function f() { return 1; }
 | |
|         assertEquals(1, f());
 | |
|       }
 | |
|       assertEquals(1, f());
 | |
|     })();
 | |
|     assertEquals(0, f);
 | |
|   }
 | |
| })();
 | |
| 
 | |
| (function notInDefaultScope() {
 | |
|   var y = 1;
 | |
|   (function innerNotInDefaultScope(x = y) {
 | |
|     assertEquals('undefined', typeof y);
 | |
|     {
 | |
|       function y() {}
 | |
|     }
 | |
|     assertEquals('function', typeof y);
 | |
|     assertEquals(1, x);
 | |
|   })();
 | |
| })();
 | |
| 
 | |
| (function noHoistingThroughNestedLexical() {
 | |
|   {
 | |
|     let f = 2;
 | |
|     {
 | |
|       let y = 3;
 | |
|       function f() {
 | |
|         y = 2;
 | |
|       }
 | |
|       f();
 | |
|       assertEquals(2, y);
 | |
|     }
 | |
|     assertEquals(2, f);
 | |
|   }
 | |
|   assertThrows(()=>f, ReferenceError);
 | |
| })();
 | |
| 
 | |
| // Only the first function is hoisted; the second is blocked by the first.
 | |
| // Contrast overridingLocalFunction, in which the outer function declaration
 | |
| // is not lexical and so the inner declaration is hoisted.
 | |
| (function noHoistingThroughNestedFunctions() {
 | |
|   assertEquals(undefined, f); // Also checks that the var-binding exists
 | |
| 
 | |
|   {
 | |
|     assertEquals(4, f());
 | |
| 
 | |
|     function f() {
 | |
|       return 4;
 | |
|     }
 | |
| 
 | |
|     {
 | |
|       assertEquals(5, f());
 | |
|       function f() {
 | |
|         return 5;
 | |
|       }
 | |
|       assertEquals(5, f());
 | |
|     }
 | |
| 
 | |
|     assertEquals(4, f());
 | |
|   }
 | |
| 
 | |
|   assertEquals(4, f());
 | |
| })();
 | |
| 
 | |
| // B.3.5 interacts with B.3.3 to allow this.
 | |
| (function hoistingThroughSimpleCatch() {
 | |
|   assertEquals(undefined, f);
 | |
| 
 | |
|   try {
 | |
|     throw 0;
 | |
|   } catch (f) {
 | |
|     {
 | |
|       assertEquals(4, f());
 | |
| 
 | |
|       function f() {
 | |
|         return 4;
 | |
|       }
 | |
| 
 | |
|       assertEquals(4, f());
 | |
|     }
 | |
| 
 | |
|     assertEquals(0, f);
 | |
|   }
 | |
| 
 | |
|   assertEquals(4, f());
 | |
| })();
 | |
| 
 | |
| (function noHoistingThroughComplexCatch() {
 | |
|   try {
 | |
|     throw 0;
 | |
|   } catch ({f}) {
 | |
|     {
 | |
|       assertEquals(4, f());
 | |
| 
 | |
|       function f() {
 | |
|         return 4;
 | |
|       }
 | |
| 
 | |
|       assertEquals(4, f());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   assertThrows(()=>f, ReferenceError);
 | |
| })();
 | |
| 
 | |
| (function hoistingThroughWith() {
 | |
|   with ({f: 0}) {
 | |
|     assertEquals(0, f);
 | |
| 
 | |
|     {
 | |
|       assertEquals(4, f());
 | |
| 
 | |
|       function f() {
 | |
|         return 4;
 | |
|       }
 | |
| 
 | |
|       assertEquals(4, f());
 | |
|     }
 | |
| 
 | |
|     assertEquals(0, f);
 | |
|   }
 | |
| 
 | |
|   assertEquals(4, f());
 | |
| })();
 | |
| 
 | |
| // 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());
 | |
|   `);
 | |
| }();
 | |
| 
 | |
| (function evalHoistingThroughSimpleCatch() {
 | |
|   try {
 | |
|     throw 0;
 | |
|   } catch (f) {
 | |
|     eval(`{ function f() {
 | |
|       return 4;
 | |
|     } }`);
 | |
| 
 | |
|     assertEquals(0, f);
 | |
|   }
 | |
| 
 | |
|   assertEquals(4, f());
 | |
| })();
 | |
| 
 | |
| (function evalHoistingThroughWith() {
 | |
|   with ({f: 0}) {
 | |
|     eval(`{ function f() {
 | |
|       return 4;
 | |
|     } }`);
 | |
| 
 | |
|     assertEquals(0, f);
 | |
|   }
 | |
| 
 | |
|   assertEquals(4, f());
 | |
| })();
 | |
| 
 | |
| let dontHoistGlobal;
 | |
| { function dontHoistGlobal() {} }
 | |
| assertEquals(undefined, dontHoistGlobal);
 | |
| 
 | |
| let dontHoistEval;
 | |
| var throws = false;
 | |
| try {
 | |
|   eval("{ function dontHoistEval() {} }");
 | |
| } catch (e) {
 | |
|   throws = true;
 | |
| }
 | |
| assertFalse(throws);
 | |
| 
 | |
| // When the global object is frozen, silently don't hoist
 | |
| // Currently this actually throws BUG(v8:4452)
 | |
| Object.freeze(this);
 | |
| {
 | |
|   let 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);
 | |
| }
 |