diff --git a/Libraries/LibJS/Bytecode/AsmInterpreter/asmint.asm b/Libraries/LibJS/Bytecode/AsmInterpreter/asmint.asm index fe3e1fe6ba2..df775e32266 100644 --- a/Libraries/LibJS/Bytecode/AsmInterpreter/asmint.asm +++ b/Libraries/LibJS/Bytecode/AsmInterpreter/asmint.asm @@ -1301,10 +1301,11 @@ handler GetCalleeAndThisFromEnvironment load64 t0, [t3, BINDINGS_DATA_PTR] mul t2, t2, SIZEOF_BINDING add t0, t2 + # TDZ state lives in Binding.initialized; the value slot itself starts as + # undefined, so checking for EMPTY would miss cached second-hit calls. + load8 t1, [t0, BINDING_INITIALIZED] + branch_zero t1, .slow load64 t1, [t0, BINDING_VALUE] - # Check value is not empty (TDZ) - mov t4, EMPTY_TAG_SHIFTED - branch_eq t1, t4, .slow store_operand m_callee, t1 # this = undefined (DeclarativeEnvironment.with_base_object() always returns nullptr) mov t0, UNDEFINED_SHIFTED diff --git a/Tests/LibJS/Runtime/regress/asmint-get-callee-from-environment-tdz-cache.js b/Tests/LibJS/Runtime/regress/asmint-get-callee-from-environment-tdz-cache.js new file mode 100644 index 00000000000..c770974de6b --- /dev/null +++ b/Tests/LibJS/Runtime/regress/asmint-get-callee-from-environment-tdz-cache.js @@ -0,0 +1,22 @@ +test("cached environment calls keep throwing TDZ reference errors", () => { + let calls = 0; + + expect(() => { + { + function g() { + try { + return f(); + } catch (error) { + ++calls; + if (calls === 1) return g(); + throw error; + } + } + + g(); + let f = 1; + } + }).toThrowWithMessage(ReferenceError, "Binding f is not initialized"); + + expect(calls).toBe(2); +});