mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 10:32:43 +00:00 
			
		
		
		
	 1341f4438d
			
		
	
	
		1341f4438d
		
	
	
	
	
		
			
			These are then restored upon `ContinuePendingUnwind`. This stops us from forgetting where we needed to jump when we do extra try-catches in finally blocks. Co-Authored-By: Jesús "gsus" Lapastora <cyber.gsuscode@gmail.com>
		
			
				
	
	
		
			448 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			448 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| test("Nested try/catch/finally with break", () => {
 | |
|     const executionOrder = [];
 | |
| 
 | |
|     try {
 | |
|         for (const i = 1337; ; expect().fail("Jumped to for loop update block")) {
 | |
|             try {
 | |
|                 try {
 | |
|                     try {
 | |
|                         break;
 | |
|                     } finally {
 | |
|                         expect(i).toBe(1337);
 | |
|                         executionOrder.push(1);
 | |
|                     }
 | |
|                 } finally {
 | |
|                     expect(i).toBe(1337);
 | |
|                     executionOrder.push(2);
 | |
|                 }
 | |
|             } finally {
 | |
|                 expect(i).toBe(1337);
 | |
|                 executionOrder.push(3);
 | |
|             }
 | |
| 
 | |
|             expect().fail("Running code after second to most outer try");
 | |
|         }
 | |
|     } finally {
 | |
|         expect(() => {
 | |
|             i;
 | |
|         }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|         executionOrder.push(4);
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         i;
 | |
|     }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|     expect(executionOrder).toEqual([1, 2, 3, 4]);
 | |
| });
 | |
| 
 | |
| test("Nested try/finally with labelled break", () => {
 | |
|     const executionOrder = [];
 | |
| 
 | |
|     outer: try {
 | |
|         const libjs = 0;
 | |
|         expect(libjs).toBe(0);
 | |
| 
 | |
|         try {
 | |
|             const serenity = 1;
 | |
|             expect(libjs).toBe(0);
 | |
|             expect(serenity).toBe(1);
 | |
| 
 | |
|             for (const i = 2; ; expect().fail("Jumped to for loop update block")) {
 | |
|                 const foo = 3;
 | |
| 
 | |
|                 expect(libjs).toBe(0);
 | |
|                 expect(serenity).toBe(1);
 | |
|                 expect(i).toBe(2);
 | |
|                 expect(foo).toBe(3);
 | |
| 
 | |
|                 try {
 | |
|                     const bar = 4;
 | |
| 
 | |
|                     expect(libjs).toBe(0);
 | |
|                     expect(serenity).toBe(1);
 | |
|                     expect(i).toBe(2);
 | |
|                     expect(foo).toBe(3);
 | |
|                     expect(bar).toBe(4);
 | |
| 
 | |
|                     try {
 | |
|                         const baz = 5;
 | |
| 
 | |
|                         expect(libjs).toBe(0);
 | |
|                         expect(serenity).toBe(1);
 | |
|                         expect(i).toBe(2);
 | |
|                         expect(foo).toBe(3);
 | |
|                         expect(bar).toBe(4);
 | |
|                         expect(baz).toBe(5);
 | |
| 
 | |
|                         try {
 | |
|                             const whf = 6;
 | |
| 
 | |
|                             expect(libjs).toBe(0);
 | |
|                             expect(serenity).toBe(1);
 | |
|                             expect(i).toBe(2);
 | |
|                             expect(foo).toBe(3);
 | |
|                             expect(bar).toBe(4);
 | |
|                             expect(baz).toBe(5);
 | |
|                             expect(whf).toBe(6);
 | |
| 
 | |
|                             break outer;
 | |
|                         } finally {
 | |
|                             const innerFinally1 = 7;
 | |
| 
 | |
|                             expect(() => {
 | |
|                                 whf;
 | |
|                             }).toThrowWithMessage(ReferenceError, "'whf' is not defined");
 | |
| 
 | |
|                             expect(libjs).toBe(0);
 | |
|                             expect(serenity).toBe(1);
 | |
|                             expect(i).toBe(2);
 | |
|                             expect(foo).toBe(3);
 | |
|                             expect(bar).toBe(4);
 | |
|                             expect(baz).toBe(5);
 | |
|                             expect(innerFinally1).toBe(7);
 | |
| 
 | |
|                             executionOrder.push(1);
 | |
|                         }
 | |
| 
 | |
|                         expect().fail("Running code after most inner try");
 | |
|                     } finally {
 | |
|                         const innerFinally2 = 8;
 | |
| 
 | |
|                         expect(() => {
 | |
|                             baz;
 | |
|                         }).toThrowWithMessage(ReferenceError, "'baz' is not defined");
 | |
| 
 | |
|                         expect(() => {
 | |
|                             whf;
 | |
|                         }).toThrowWithMessage(ReferenceError, "'whf' is not defined");
 | |
| 
 | |
|                         expect(() => {
 | |
|                             innerFinally1;
 | |
|                         }).toThrowWithMessage(ReferenceError, "'innerFinally1' is not defined");
 | |
| 
 | |
|                         expect(libjs).toBe(0);
 | |
|                         expect(serenity).toBe(1);
 | |
|                         expect(i).toBe(2);
 | |
|                         expect(foo).toBe(3);
 | |
|                         expect(bar).toBe(4);
 | |
|                         expect(innerFinally2).toBe(8);
 | |
| 
 | |
|                         executionOrder.push(2);
 | |
|                     }
 | |
| 
 | |
|                     expect().fail("Running code after second to most inner try");
 | |
|                 } finally {
 | |
|                     const innerFinally3 = 9;
 | |
| 
 | |
|                     expect(() => {
 | |
|                         bar;
 | |
|                     }).toThrowWithMessage(ReferenceError, "'bar' is not defined");
 | |
| 
 | |
|                     expect(() => {
 | |
|                         baz;
 | |
|                     }).toThrowWithMessage(ReferenceError, "'baz' is not defined");
 | |
| 
 | |
|                     expect(() => {
 | |
|                         whf;
 | |
|                     }).toThrowWithMessage(ReferenceError, "'whf' is not defined");
 | |
| 
 | |
|                     expect(() => {
 | |
|                         innerFinally1;
 | |
|                     }).toThrowWithMessage(ReferenceError, "'innerFinally1' is not defined");
 | |
| 
 | |
|                     expect(() => {
 | |
|                         innerFinally2;
 | |
|                     }).toThrowWithMessage(ReferenceError, "'innerFinally2' is not defined");
 | |
| 
 | |
|                     expect(libjs).toBe(0);
 | |
|                     expect(serenity).toBe(1);
 | |
|                     expect(i).toBe(2);
 | |
|                     expect(foo).toBe(3);
 | |
|                     expect(innerFinally3).toBe(9);
 | |
| 
 | |
|                     executionOrder.push(3);
 | |
|                 }
 | |
| 
 | |
|                 expect().fail("Running code after third to most inner try");
 | |
|             }
 | |
| 
 | |
|             expect().fail("Running code after for loop");
 | |
|         } finally {
 | |
|             const innerFinally4 = 10;
 | |
| 
 | |
|             expect(() => {
 | |
|                 serenity;
 | |
|             }).toThrowWithMessage(ReferenceError, "'serenity' is not defined");
 | |
| 
 | |
|             expect(() => {
 | |
|                 i;
 | |
|             }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|             expect(() => {
 | |
|                 foo;
 | |
|             }).toThrowWithMessage(ReferenceError, "'foo' is not defined");
 | |
| 
 | |
|             expect(() => {
 | |
|                 bar;
 | |
|             }).toThrowWithMessage(ReferenceError, "'bar' is not defined");
 | |
| 
 | |
|             expect(() => {
 | |
|                 baz;
 | |
|             }).toThrowWithMessage(ReferenceError, "'baz' is not defined");
 | |
| 
 | |
|             expect(() => {
 | |
|                 whf;
 | |
|             }).toThrowWithMessage(ReferenceError, "'whf' is not defined");
 | |
| 
 | |
|             expect(() => {
 | |
|                 innerFinally1;
 | |
|             }).toThrowWithMessage(ReferenceError, "'innerFinally1' is not defined");
 | |
| 
 | |
|             expect(() => {
 | |
|                 innerFinally2;
 | |
|             }).toThrowWithMessage(ReferenceError, "'innerFinally2' is not defined");
 | |
| 
 | |
|             expect(() => {
 | |
|                 innerFinally3;
 | |
|             }).toThrowWithMessage(ReferenceError, "'innerFinally3' is not defined");
 | |
| 
 | |
|             expect(libjs).toBe(0);
 | |
|             expect(innerFinally4).toBe(10);
 | |
| 
 | |
|             executionOrder.push(4);
 | |
|         }
 | |
| 
 | |
|         expect().fail("Running code after second to outer try");
 | |
|     } finally {
 | |
|         const innerFinally5 = 11;
 | |
| 
 | |
|         expect(() => {
 | |
|             libjs;
 | |
|         }).toThrowWithMessage(ReferenceError, "'libjs' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             serenity;
 | |
|         }).toThrowWithMessage(ReferenceError, "'serenity' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             i;
 | |
|         }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             foo;
 | |
|         }).toThrowWithMessage(ReferenceError, "'foo' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             bar;
 | |
|         }).toThrowWithMessage(ReferenceError, "'bar' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             baz;
 | |
|         }).toThrowWithMessage(ReferenceError, "'baz' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             whf;
 | |
|         }).toThrowWithMessage(ReferenceError, "'whf' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             innerFinally1;
 | |
|         }).toThrowWithMessage(ReferenceError, "'innerFinally1' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             innerFinally2;
 | |
|         }).toThrowWithMessage(ReferenceError, "'innerFinally2' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             innerFinally3;
 | |
|         }).toThrowWithMessage(ReferenceError, "'innerFinally3' is not defined");
 | |
| 
 | |
|         expect(() => {
 | |
|             innerFinally4;
 | |
|         }).toThrowWithMessage(ReferenceError, "'innerFinally4' is not defined");
 | |
| 
 | |
|         expect(innerFinally5).toBe(11);
 | |
| 
 | |
|         executionOrder.push(5);
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         libjs;
 | |
|     }).toThrowWithMessage(ReferenceError, "'libjs' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         serenity;
 | |
|     }).toThrowWithMessage(ReferenceError, "'serenity' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         i;
 | |
|     }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         foo;
 | |
|     }).toThrowWithMessage(ReferenceError, "'foo' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         bar;
 | |
|     }).toThrowWithMessage(ReferenceError, "'bar' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         baz;
 | |
|     }).toThrowWithMessage(ReferenceError, "'baz' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         whf;
 | |
|     }).toThrowWithMessage(ReferenceError, "'whf' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         innerFinally1;
 | |
|     }).toThrowWithMessage(ReferenceError, "'innerFinally1' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         innerFinally2;
 | |
|     }).toThrowWithMessage(ReferenceError, "'innerFinally2' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         innerFinally3;
 | |
|     }).toThrowWithMessage(ReferenceError, "'innerFinally3' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         innerFinally4;
 | |
|     }).toThrowWithMessage(ReferenceError, "'innerFinally4' is not defined");
 | |
| 
 | |
|     expect(() => {
 | |
|         innerFinally5;
 | |
|     }).toThrowWithMessage(ReferenceError, "'innerFinally5' is not defined");
 | |
| 
 | |
|     expect(executionOrder).toEqual([1, 2, 3, 4, 5]);
 | |
| });
 | |
| 
 | |
| test("labelled break in finally overrides labelled break in try", () => {
 | |
|     const executionOrder = [];
 | |
| 
 | |
|     outer: for (const i = 1; ; expect().fail("Jumped to outer for loop update block")) {
 | |
|         inner: for (const j = 2; ; expect().fail("Jumped to inner for loop update block")) {
 | |
|             try {
 | |
|                 executionOrder.push(1);
 | |
|                 break inner;
 | |
|             } finally {
 | |
|                 executionOrder.push(2);
 | |
|                 break outer;
 | |
|             }
 | |
| 
 | |
|             expect().fail("Running code after try block");
 | |
|         }
 | |
| 
 | |
|         expect().fail("Running code after inner for loop");
 | |
|     }
 | |
| 
 | |
|     expect(executionOrder).toEqual([1, 2]);
 | |
| });
 | |
| 
 | |
| test("Throw while breaking", () => {
 | |
|     const executionOrder = [];
 | |
|     try {
 | |
|         for (const i = 1337; ; expect().fail("Jumped to for loop update block")) {
 | |
|             try {
 | |
|                 executionOrder.push(1);
 | |
|                 break;
 | |
|             } finally {
 | |
|                 executionOrder.push(2);
 | |
|                 throw 1;
 | |
|             }
 | |
|         }
 | |
|     } finally {
 | |
|         executionOrder.push(3);
 | |
|     }
 | |
|     expect(() => {
 | |
|         i;
 | |
|     }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|     expect(executionOrder).toEqual([1, 2, 3]);
 | |
| });
 | |
| 
 | |
| test("Throw while breaking with nested try-catch in finalizer", () => {
 | |
|     const executionOrder = [];
 | |
|     try {
 | |
|         for (const i = 1337; ; expect().fail("Jumped to for loop update block")) {
 | |
|             try {
 | |
|                 executionOrder.push(1);
 | |
|                 break;
 | |
|             } finally {
 | |
|                 try {
 | |
|                     throw 1;
 | |
|                 } catch {
 | |
|                     executionOrder.push(2);
 | |
|                 }
 | |
|                 executionOrder.push(3);
 | |
|             }
 | |
|             expect().fail("Jumped out of inner finally statement");
 | |
|         }
 | |
|     } finally {
 | |
|         executionOrder.push(4);
 | |
|     }
 | |
|     expect(() => {
 | |
|         i;
 | |
|     }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|     expect(executionOrder).toEqual([1, 2, 3, 4]);
 | |
| });
 | |
| 
 | |
| test("Throw while breaking with nested try-catch-finally in finalizer", () => {
 | |
|     const executionOrder = [];
 | |
|     try {
 | |
|         for (const i = 1337; ; expect().fail("Jumped to for loop update block")) {
 | |
|             try {
 | |
|                 executionOrder.push(1);
 | |
|                 break;
 | |
|             } finally {
 | |
|                 try {
 | |
|                     executionOrder.push(2);
 | |
|                 } catch {
 | |
|                     expect.fail("Entered catch");
 | |
|                 } finally {
 | |
|                     executionOrder.push(3);
 | |
|                 }
 | |
|                 executionOrder.push(4);
 | |
|             }
 | |
|             expect().fail("Jumped out of inner finally statement");
 | |
|         }
 | |
|     } finally {
 | |
|         executionOrder.push(5);
 | |
|     }
 | |
|     expect(() => {
 | |
|         i;
 | |
|     }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|     expect(executionOrder).toEqual([1, 2, 3, 4, 5]);
 | |
| });
 | |
| 
 | |
| test("Throw while breaking with nested try-catch-finally with throw in finalizer", () => {
 | |
|     const executionOrder = [];
 | |
|     try {
 | |
|         for (const i = 1337; ; expect().fail("Jumped to for loop update block")) {
 | |
|             try {
 | |
|                 executionOrder.push(1);
 | |
|                 break;
 | |
|             } finally {
 | |
|                 try {
 | |
|                     throw 1;
 | |
|                 } catch {
 | |
|                     executionOrder.push(2);
 | |
|                 } finally {
 | |
|                     executionOrder.push(3);
 | |
|                 }
 | |
|                 executionOrder.push(4);
 | |
|             }
 | |
|             expect().fail("Jumped out of inner finally statement");
 | |
|         }
 | |
|     } finally {
 | |
|         executionOrder.push(5);
 | |
|     }
 | |
|     expect(() => {
 | |
|         i;
 | |
|     }).toThrowWithMessage(ReferenceError, "'i' is not defined");
 | |
| 
 | |
|     expect(executionOrder).toEqual([1, 2, 3, 4, 5]);
 | |
| });
 |