mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 08:12:43 +00:00 
			
		
		
		
	 ae349ec6a8
			
		
	
	
		ae349ec6a8
		
	
	
	
	
		
			
			We already did this but it called the @@iterator method of %Array.prototype% visible to the user for example by overriding that method. This should not be visible so we use a special version of SuperCall now.
		
			
				
	
	
		
			508 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			508 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| test("method inheritance", () => {
 | |
|     class Parent {
 | |
|         method() {
 | |
|             return 3;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Child extends Parent {}
 | |
| 
 | |
|     const p = new Parent();
 | |
|     const c = new Child();
 | |
|     expect(p.method()).toBe(3);
 | |
|     expect(c.method()).toBe(3);
 | |
| });
 | |
| 
 | |
| test("method overriding", () => {
 | |
|     class Parent {
 | |
|         method() {
 | |
|             return 3;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Child extends Parent {
 | |
|         method() {
 | |
|             return 10;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const p = new Parent();
 | |
|     const c = new Child();
 | |
|     expect(p.method()).toBe(3);
 | |
|     expect(c.method()).toBe(10);
 | |
| });
 | |
| 
 | |
| test("parent method reference with super", () => {
 | |
|     class Parent {
 | |
|         method() {
 | |
|             return 3;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Child extends Parent {
 | |
|         method() {
 | |
|             return super.method() * 2;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const p = new Parent();
 | |
|     const c = new Child();
 | |
|     expect(p.method()).toBe(3);
 | |
|     expect(c.method()).toBe(6);
 | |
| });
 | |
| 
 | |
| test("child class access to parent class initialized properties", () => {
 | |
|     class Parent {
 | |
|         constructor() {
 | |
|             this.x = 3;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Child extends Parent {}
 | |
| 
 | |
|     const p = new Parent();
 | |
|     const c = new Child();
 | |
|     expect(p.x).toBe(3);
 | |
|     expect(c.x).toBe(3);
 | |
| });
 | |
| 
 | |
| test("child class modification of parent class properties", () => {
 | |
|     class Parent {
 | |
|         constructor() {
 | |
|             this.x = 3;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Child extends Parent {
 | |
|         change() {
 | |
|             this.x = 10;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const p = new Parent();
 | |
|     const c = new Child();
 | |
|     expect(p.x).toBe(3);
 | |
|     expect(c.x).toBe(3);
 | |
| 
 | |
|     c.change();
 | |
|     expect(c.x).toBe(10);
 | |
| });
 | |
| 
 | |
| test("inheritance and hasOwnProperty", () => {
 | |
|     class Parent {
 | |
|         constructor() {
 | |
|             this.x = 3;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Child extends Parent {
 | |
|         method() {
 | |
|             this.y = 10;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const p = new Parent();
 | |
|     const c = new Child();
 | |
|     expect(p.hasOwnProperty("x")).toBeTrue();
 | |
|     expect(p.hasOwnProperty("y")).toBeFalse();
 | |
|     expect(c.hasOwnProperty("x")).toBeTrue();
 | |
|     expect(c.hasOwnProperty("y")).toBeFalse();
 | |
| 
 | |
|     c.method();
 | |
|     expect(c.hasOwnProperty("x")).toBeTrue();
 | |
|     expect(c.hasOwnProperty("y")).toBeTrue();
 | |
| });
 | |
| 
 | |
| test("super constructor call from child class with argument", () => {
 | |
|     class Parent {
 | |
|         constructor(x) {
 | |
|             this.x = x;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Child extends Parent {
 | |
|         constructor() {
 | |
|             super(10);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const p = new Parent(3);
 | |
|     const c = new Child(3);
 | |
|     expect(p.x).toBe(3);
 | |
|     expect(c.x).toBe(10);
 | |
| });
 | |
| 
 | |
| test("advanced 'extends' RHS", () => {
 | |
|     const foo = {
 | |
|         bar() {
 | |
|             return {
 | |
|                 baz() {
 | |
|                     return function () {
 | |
|                         return function () {
 | |
|                             return { quux: Number };
 | |
|                         };
 | |
|                     };
 | |
|                 },
 | |
|             };
 | |
|         },
 | |
|     };
 | |
|     class Foo extends foo.bar()["baz"]()`qux`().quux {}
 | |
|     expect(new Foo()).toBeInstanceOf(Number);
 | |
| });
 | |
| 
 | |
| test("issue #7045, super constructor call from child class in catch {}", () => {
 | |
|     class Parent {
 | |
|         constructor(x) {
 | |
|             this.x = x;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Child extends Parent {
 | |
|         constructor() {
 | |
|             try {
 | |
|                 throw new Error("Error in Child constructor");
 | |
|             } catch (e) {
 | |
|                 super(e.message);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const c = new Child();
 | |
|     expect(c.x).toBe("Error in Child constructor");
 | |
| });
 | |
| 
 | |
| test("Issue #7044, super property access before super() call", () => {
 | |
|     class Foo {
 | |
|         constructor() {
 | |
|             super.bar;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     new Foo();
 | |
| });
 | |
| 
 | |
| test("Issue #8574, super property access before super() call", () => {
 | |
|     var hit = false;
 | |
| 
 | |
|     class Foo extends Object {
 | |
|         constructor() {
 | |
|             expect(() => {
 | |
|                 const foo = super.bar();
 | |
|             }).toThrowWithMessage(ReferenceError, "|this| has not been initialized");
 | |
|             hit = true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Note: We catch two exceptions here.
 | |
|     expect(() => {
 | |
|         new Foo();
 | |
|     }).toThrowWithMessage(ReferenceError, "|this| has not been initialized");
 | |
|     expect(hit).toBeTrue();
 | |
| });
 | |
| 
 | |
| test("can access super via direct eval", () => {
 | |
|     let superCalled = false;
 | |
|     const aObject = { a: 1 };
 | |
|     const bObject = { b: 2 };
 | |
| 
 | |
|     class A {
 | |
|         constructor() {
 | |
|             superCalled = true;
 | |
|         }
 | |
| 
 | |
|         foo() {
 | |
|             return aObject;
 | |
|         }
 | |
| 
 | |
|         bar() {
 | |
|             return bObject;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class B extends A {
 | |
|         constructor() {
 | |
|             eval("super()");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new B();
 | |
|     }).not.toThrow();
 | |
| 
 | |
|     expect(superCalled).toBeTrue();
 | |
|     superCalled = false;
 | |
| 
 | |
|     class C extends A {
 | |
|         constructor() {
 | |
|             eval("super()");
 | |
|             return eval("super.foo()");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new C();
 | |
|     }).not.toThrow();
 | |
| 
 | |
|     expect(superCalled).toBeTrue();
 | |
|     superCalled = false;
 | |
| 
 | |
|     expect(new C()).toBe(aObject);
 | |
| 
 | |
|     expect(superCalled).toBeTrue();
 | |
|     superCalled = false;
 | |
| 
 | |
|     class D extends A {
 | |
|         constructor() {
 | |
|             eval("super()");
 | |
|             return eval("super['bar']()");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new D();
 | |
|     }).not.toThrow();
 | |
| 
 | |
|     expect(superCalled).toBeTrue();
 | |
|     superCalled = false;
 | |
| 
 | |
|     expect(new D()).toBe(bObject);
 | |
| 
 | |
|     expect(superCalled).toBeTrue();
 | |
| });
 | |
| 
 | |
| test("cannot access super via indirect eval", () => {
 | |
|     const indirect = eval;
 | |
|     let superCalled = false;
 | |
| 
 | |
|     const aObject = { a: 1 };
 | |
|     const bObject = { b: 1 };
 | |
| 
 | |
|     class A {
 | |
|         constructor() {
 | |
|             superCalled = true;
 | |
|             this.a = aObject;
 | |
|             this.c = bObject;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class B extends A {
 | |
|         constructor() {
 | |
|             indirect("super()");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new B();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(superCalled).toBeFalse();
 | |
| 
 | |
|     class C extends A {
 | |
|         constructor() {
 | |
|             super();
 | |
|             return indirect("super.a");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new C();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(superCalled).toBeTrue();
 | |
|     superCalled = false;
 | |
| 
 | |
|     class D extends A {
 | |
|         constructor() {
 | |
|             super();
 | |
|             return indirect("super['b']");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new D();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(superCalled).toBeTrue();
 | |
| });
 | |
| 
 | |
| test("super outside of derived class fails to parse", () => {
 | |
|     expect("super").not.toEval();
 | |
|     expect("super()").not.toEval();
 | |
|     expect("super.a").not.toEval();
 | |
|     expect("super['b']").not.toEval();
 | |
|     expect("function a() { super }").not.toEval();
 | |
|     expect("function a() { super() }").not.toEval();
 | |
|     expect("function a() { super.a }").not.toEval();
 | |
|     expect("function a() { super['b'] }").not.toEval();
 | |
|     expect("() => { super }").not.toEval();
 | |
|     expect("() => { super() }").not.toEval();
 | |
|     expect("() => { super.a }").not.toEval();
 | |
|     expect("() => { super['b'] }").not.toEval();
 | |
|     expect("class A { constructor() { super } }").not.toEval();
 | |
|     expect("class A { constructor() { super() } }").not.toEval();
 | |
| 
 | |
|     expect(() => {
 | |
|         eval("super");
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(() => {
 | |
|         eval("super()");
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(() => {
 | |
|         eval("super.a");
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(() => {
 | |
|         eval("super['b']");
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     function a() {
 | |
|         eval("super");
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         a();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(() => {
 | |
|         new a();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     function b() {
 | |
|         eval("super()");
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         b();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(() => {
 | |
|         new b();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     function c() {
 | |
|         eval("super.a");
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         c();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(() => {
 | |
|         new c();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     function d() {
 | |
|         eval("super['b']");
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         d();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     expect(() => {
 | |
|         new d();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     const e = () => eval("super");
 | |
| 
 | |
|     expect(() => {
 | |
|         e();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     const f = () => eval("super()");
 | |
| 
 | |
|     expect(() => {
 | |
|         f();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     const g = () => eval("super.a");
 | |
| 
 | |
|     expect(() => {
 | |
|         g();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     const h = () => eval("super['b']");
 | |
| 
 | |
|     expect(() => {
 | |
|         h();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     class I {
 | |
|         constructor() {
 | |
|             eval("super");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new I();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| 
 | |
|     class J {
 | |
|         constructor() {
 | |
|             eval("super()");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new J();
 | |
|     }).toThrowWithMessage(SyntaxError, "'super' keyword unexpected here");
 | |
| });
 | |
| 
 | |
| test("When no constructor on deriving class @@iterator of %Array.prototype% is not visibly called", () => {
 | |
|     const oldIterator = Array.prototype[Symbol.iterator];
 | |
|     var calls = 0;
 | |
|     Array.prototype[Symbol.iterator] = function () {
 | |
|         ++calls;
 | |
|         expect().fail("Called @@iterator");
 | |
|     };
 | |
| 
 | |
|     class Base {
 | |
|         constructor(value1, value2) {
 | |
|             this.value1 = value1;
 | |
|             this.value2 = value2;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class Derived extends Base {}
 | |
| 
 | |
|     const noArgumentsDerived = new Derived();
 | |
|     expect(noArgumentsDerived.value1).toBeUndefined();
 | |
|     expect(noArgumentsDerived.value2).toBeUndefined();
 | |
|     expect(noArgumentsDerived).toBeInstanceOf(Base);
 | |
|     expect(noArgumentsDerived).toBeInstanceOf(Derived);
 | |
| 
 | |
|     const singleArgumentDerived = new Derived(1);
 | |
|     expect(singleArgumentDerived.value1).toBe(1);
 | |
|     expect(singleArgumentDerived.value2).toBeUndefined();
 | |
| 
 | |
|     const singleArrayArgumentDerived = new Derived([1, 2]);
 | |
|     expect(singleArrayArgumentDerived.value1).toEqual([1, 2]);
 | |
|     expect(singleArrayArgumentDerived.value2).toBeUndefined();
 | |
| 
 | |
|     const doubleArgumentDerived = new Derived(1, 2);
 | |
|     expect(doubleArgumentDerived.value1).toBe(1);
 | |
|     expect(doubleArgumentDerived.value2).toBe(2);
 | |
| 
 | |
|     expect(calls).toBe(0);
 | |
| 
 | |
|     class Derived2 extends Base {
 | |
|         constructor(...args) {
 | |
|             super(...args);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     expect(() => {
 | |
|         new Derived2();
 | |
|     }).toThrowWithMessage(ExpectationError, "Called @@iterator");
 | |
| 
 | |
|     expect(calls).toBe(1);
 | |
| 
 | |
|     Array.prototype[Symbol.iterator] = oldIterator;
 | |
| 
 | |
|     // Now Derived2 is fine again.
 | |
|     expect(new Derived2()).toBeInstanceOf(Derived2);
 | |
| 
 | |
|     expect(calls).toBe(1);
 | |
| });
 |