diff --git a/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp b/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp index a26db38685..ab4bf8544e 100644 --- a/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp +++ b/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp @@ -139,6 +139,9 @@ ThrowCompletionOr NativeFunction::internal_call(Value this_argument, Mark callee_context.lexical_environment = caller_context.lexical_environment; callee_context.variable_environment = caller_context.variable_environment; + // Note: Keeping the private environment is probably only needed because of async methods in classes + // calling async_block_start which goes through a NativeFunction here. + callee_context.private_environment = caller_context.private_environment; // NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller. callee_context.is_strict_mode = vm.in_strict_mode(); diff --git a/Userland/Libraries/LibJS/Tests/classes/class-methods.js b/Userland/Libraries/LibJS/Tests/classes/class-methods.js index d300c555fe..e99eba212e 100644 --- a/Userland/Libraries/LibJS/Tests/classes/class-methods.js +++ b/Userland/Libraries/LibJS/Tests/classes/class-methods.js @@ -61,3 +61,66 @@ test("method named 'async'", () => { expect("async" in a).toBeTrue(); expect(a.async()).toBe("function named async"); }); + +test("can call other private methods from methods", () => { + class A { + #a() { + return 1; + } + + async #b() { + return 2; + } + + syncA() { + return this.#a(); + } + + async asyncA() { + return this.#a(); + } + + syncB() { + return this.#b(); + } + + async asyncB() { + return this.#b(); + } + } + + var called = false; + + async function check() { + called = true; + const a = new A(); + + 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(); +}); diff --git a/Userland/Libraries/LibJS/Tests/classes/class-static.js b/Userland/Libraries/LibJS/Tests/classes/class-static.js index cb2436cf33..28bed5b489 100644 --- a/Userland/Libraries/LibJS/Tests/classes/class-static.js +++ b/Userland/Libraries/LibJS/Tests/classes/class-static.js @@ -81,3 +81,64 @@ test("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(); +});