ladybird/Tests/LibJS/Runtime/classes/class-static.js
Andreas Kling 991ba90d40 LibJS: Make class prototype property non-writable
ClassDefinitionEvaluation calls MakeConstructor with false for the
writablePrototype argument, so class constructors get a non-writable,
non-enumerable, non-configurable prototype property. We were using the
ordinary function attributes instead, which also allowed static class
elements named "prototype" to redefine the property.

Add LibJS coverage for the constructor and prototype descriptors, and
for static methods/accessors that attempt to define "prototype".
2026-05-22 01:56:57 +02:00

164 lines
3.1 KiB
JavaScript

test("basic functionality", () => {
class A {
static method() {
return 10;
}
}
expect(A.method()).toBe(10);
expect(new A().method).toBeUndefined();
});
test("extended name syntax", () => {
class A {
static method() {
return 1;
}
static 12() {
return 2;
}
static [`he${"llo"}`]() {
return 3;
}
}
expect(A.method()).toBe(1);
expect(A[12]()).toBe(2);
expect(A.hello()).toBe(3);
});
test("bound |this|", () => {
class A {
static method() {
expect(this).toBe(A);
}
}
A.method();
});
test("inherited static methods", () => {
class Parent {
static method() {
return 3;
}
}
class Child extends Parent {}
expect(Parent.method()).toBe(3);
expect(Child.method()).toBe(3);
expect(new Parent()).not.toHaveProperty("method");
expect(new Child()).not.toHaveProperty("method");
});
test("static method overriding", () => {
class Parent {
static method() {
return 3;
}
}
class Child extends Parent {
static method() {
return 10;
}
}
expect(Parent.method()).toBe(3);
expect(Child.method()).toBe(10);
});
test("static elements cannot redefine prototype property", () => {
expect(() => {
class A {
static ["prototype"]() {}
}
}).toThrow(TypeError);
expect(() => {
class A {
static get ["prototype"]() {}
}
}).toThrow(TypeError);
expect(() => {
class A {
static set ["prototype"](value) {}
}
}).toThrow(TypeError);
});
test("static function named 'async'", () => {
class A {
static async() {
return "static function named async";
}
}
expect("async" in A).toBeTrue();
expect(A.async()).toBe("static function named async");
});
test("can call other private methods from static method", () => {
class A {
static #a() {
return 1;
}
static async #b() {
return 2;
}
static syncA() {
return this.#a();
}
static async asyncA() {
return this.#a();
}
static syncB() {
return this.#b();
}
static async asyncB() {
return this.#b();
}
}
var called = false;
async function check() {
called = true;
expect(A.syncA()).toBe(1);
expect(await A.asyncA()).toBe(1);
expect(await A.syncB()).toBe(2);
expect(await A.asyncB()).toBe(2);
return 3;
}
var error = null;
var failed = false;
check().then(
value => {
expect(called).toBeTrue();
expect(value).toBe(3);
},
thrownError => {
failed = true;
error = thrownError;
}
);
runQueuedPromiseJobs();
expect(called).toBeTrue();
if (failed) throw error;
expect(failed).toBeFalse();
});