From 385c2f2eb8491c43a4051962aadcc8865ff7f76b Mon Sep 17 00:00:00 2001 From: davidot Date: Tue, 15 Nov 2022 01:18:08 +0100 Subject: [PATCH] LibJS: Use correct this value for tagged template calls This has to get quite messy because we currently do evaluation to value and reference separately meaning we have to deal with a lot of edge cases here. --- Userland/Libraries/LibJS/AST.cpp | 27 ++++++++++++++++--- .../LibJS/Tests/tagged-template-literals.js | 13 +++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index b4763f6a61..b07b6c09e8 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -3627,13 +3627,32 @@ Completion TaggedTemplateLiteral::execute(Interpreter& interpreter) const // NOTE: This is both // MemberExpression : MemberExpression TemplateLiteral // CallExpression : CallExpression TemplateLiteral - // As the only difference is the first step. // 1. Let tagRef be ? Evaluation of MemberExpression. // 1. Let tagRef be ? Evaluation of CallExpression. - // 2. Let tagFunc be ? GetValue(tagRef). - auto tag = TRY(m_tag->execute(interpreter)).release_value(); + // NOTE: This is much more complicated than the spec because we have to + // handle every type of reference. If we handle evaluation closer + // to the spec this could be improved. + Value tag_this_value; + Value tag; + if (auto tag_reference = TRY(m_tag->to_reference(interpreter)); tag_reference.is_valid_reference()) { + tag = TRY(tag_reference.get_value(vm)); + if (tag_reference.is_environment_reference()) { + auto& environment = tag_reference.base_environment(); + if (environment.has_this_binding()) + tag_this_value = TRY(environment.get_this_binding(vm)); + else + tag_this_value = js_undefined(); + } else { + tag_this_value = tag_reference.get_this_value(); + } + } else { + auto result = TRY(m_tag->execute(interpreter)); + VERIFY(result.has_value()); + tag = result.release_value(); + tag_this_value = js_undefined(); + } // 3. Let thisCall be this CallExpression. // 3. Let thisCall be this MemberExpression. @@ -3655,7 +3674,7 @@ Completion TaggedTemplateLiteral::execute(Interpreter& interpreter) const arguments.append(TRY(expressions[i].execute(interpreter)).release_value()); // 5. Return ? EvaluateCall(tagFunc, tagRef, TemplateLiteral, tailCall). - return call(vm, tag, js_undefined(), move(arguments)); + return call(vm, tag, tag_this_value, move(arguments)); } // 13.2.8.3 GetTemplateObject ( templateLiteral ), https://tc39.es/ecma262/#sec-gettemplateobject diff --git a/Userland/Libraries/LibJS/Tests/tagged-template-literals.js b/Userland/Libraries/LibJS/Tests/tagged-template-literals.js index 620566a1a3..a967e0d308 100644 --- a/Userland/Libraries/LibJS/Tests/tagged-template-literals.js +++ b/Userland/Libraries/LibJS/Tests/tagged-template-literals.js @@ -163,4 +163,17 @@ describe("tagged template literal functionality", () => { let secondResult = call(value => value, 2); expect(firstResult).toBe(secondResult); }); + + test("this value of call comes from reference", () => { + let thisValue = null; + const obj = { + func() { + thisValue = this; + }, + }; + + obj.func``; + + expect(thisValue).toBe(obj); + }); });