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