mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 14:02:46 +00:00 
			
		
		
		
	 30ab198b40
			
		
	
	
		30ab198b40
		
	
	
	
	
		
			
			Our implementation of environment.CreateImmutableBinding(name, true) in this AO was not correctly initializing const variables in strict mode. This would mean that constant declarations in for loop bodies would not throw if they were modified. To fix this, add a new parameter to CreateVariable to set strict mode. Also remove the vm.is_strict mode check here, as it doesn't look like anywhere in the spec will change strict mode depending on whether the script itself is running in script mode or not. This fixes two of our test-js tests, no change to test262.
		
			
				
	
	
		
			155 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| describe("correct behavior", () => {
 | |
|     test("iterate through array", () => {
 | |
|         const a = [];
 | |
|         for (const num of [1, 2, 3]) {
 | |
|             a.push(num);
 | |
|         }
 | |
|         expect(a).toEqual([1, 2, 3]);
 | |
|     });
 | |
| 
 | |
|     test("iterate through string", () => {
 | |
|         const a = [];
 | |
|         for (const char of "hello") {
 | |
|             a.push(char);
 | |
|         }
 | |
|         expect(a).toEqual(["h", "e", "l", "l", "o"]);
 | |
|     });
 | |
| 
 | |
|     test("iterate through string object", () => {
 | |
|         const a = [];
 | |
|         for (const char of new String("hello")) {
 | |
|             a.push(char);
 | |
|         }
 | |
|         expect(a).toEqual(["h", "e", "l", "l", "o"]);
 | |
|     });
 | |
| 
 | |
|     test("use already-declared variable", () => {
 | |
|         var char;
 | |
|         for (char of "abc");
 | |
|         expect(char).toBe("c");
 | |
|     });
 | |
| 
 | |
|     test("respects custom Symbol.iterator method", () => {
 | |
|         const o = {
 | |
|             [Symbol.iterator]() {
 | |
|                 return {
 | |
|                     i: 0,
 | |
|                     next() {
 | |
|                         if (this.i++ == 3) {
 | |
|                             return { done: true };
 | |
|                         }
 | |
|                         return { value: this.i, done: false };
 | |
|                     },
 | |
|                 };
 | |
|             },
 | |
|         };
 | |
| 
 | |
|         const a = [];
 | |
|         for (const k of o) {
 | |
|             a.push(k);
 | |
|         }
 | |
| 
 | |
|         expect(a).toEqual([1, 2, 3]);
 | |
|     });
 | |
| 
 | |
|     test("loops through custom iterator if there is an exception thrown part way through", () => {
 | |
|         // This tests against the way custom iterators used to be implemented, where the values
 | |
|         // were all collected at once before the for-of body was executed, instead of getting
 | |
|         // the values one at a time
 | |
|         const o = {
 | |
|             [Symbol.iterator]() {
 | |
|                 return {
 | |
|                     i: 0,
 | |
|                     next() {
 | |
|                         if (this.i++ === 3) {
 | |
|                             throw new Error();
 | |
|                         }
 | |
|                         return { value: this.i };
 | |
|                     },
 | |
|                 };
 | |
|             },
 | |
|         };
 | |
| 
 | |
|         const a = [];
 | |
| 
 | |
|         try {
 | |
|             for (let k of o) {
 | |
|                 a.push(k);
 | |
|             }
 | |
|             expect().fail();
 | |
|         } catch (e) {
 | |
|             expect(a).toEqual([1, 2, 3]);
 | |
|         }
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe("errors", () => {
 | |
|     test("right hand side is a primitive", () => {
 | |
|         expect(() => {
 | |
|             for (const _ of 123) {
 | |
|             }
 | |
|         }).toThrowWithMessage(TypeError, "123 is not iterable");
 | |
|     });
 | |
| 
 | |
|     test("right hand side is an object", () => {
 | |
|         expect(() => {
 | |
|             for (const _ of { foo: 1, bar: 2 }) {
 | |
|             }
 | |
|         }).toThrowWithMessage(TypeError, "[object Object] is not iterable");
 | |
|     });
 | |
| });
 | |
| 
 | |
| test("allow binding patterns", () => {
 | |
|     let counter = 0;
 | |
|     for (let [a, b] of [
 | |
|         [1, 2],
 | |
|         [3, 4],
 | |
|         [5, 6],
 | |
|     ]) {
 | |
|         expect(a + 1).toBe(b);
 | |
|         counter++;
 | |
|     }
 | |
|     expect(counter).toBe(3);
 | |
| });
 | |
| 
 | |
| describe("special left hand sides", () => {
 | |
|     test("allow member expression as variable", () => {
 | |
|         const f = {};
 | |
|         for (f.a of "abc");
 | |
|         expect(f.a).toBe("c");
 | |
|     });
 | |
| 
 | |
|     test("allow member expression of function call", () => {
 | |
|         const b = {};
 | |
|         function f() {
 | |
|             return b;
 | |
|         }
 | |
| 
 | |
|         for (f().a of "abc");
 | |
| 
 | |
|         expect(f().a).toBe("c");
 | |
|     });
 | |
| 
 | |
|     test.xfail("call function is allowed in parsing but fails in runtime", () => {
 | |
|         function f() {
 | |
|             expect().fail();
 | |
|         }
 | |
| 
 | |
|         // Does not fail since it does not iterate but prettier does not like it so we use eval.
 | |
|         expect("for (f() of []);").toEvalTo(undefined);
 | |
| 
 | |
|         expect(() => {
 | |
|             eval("for (f() of [0]) { expect().fail() }");
 | |
|         }).toThrowWithMessage(ReferenceError, "Invalid left-hand side in assignment");
 | |
|     });
 | |
| 
 | |
|     test("Cannot change constant declaration in body", () => {
 | |
|         const vals = [];
 | |
|         for (const v of [1, 2]) {
 | |
|             expect(() => v++).toThrowWithMessage(TypeError, "Invalid assignment to const variable");
 | |
|             vals.push(v);
 | |
|         }
 | |
| 
 | |
|         expect(vals).toEqual([1, 2]);
 | |
|     });
 | |
| });
 |