LibJS: Use correct this value for tagged template literals with members

Required by creepjs, which does Date().split` `[3] to get the current
year.
This commit is contained in:
Luke Wilde 2025-01-16 17:18:19 +00:00 committed by Andreas Kling
parent 5f33383a7b
commit a588756105
Notes: github-actions[bot] 2025-01-17 16:16:08 +00:00
2 changed files with 119 additions and 3 deletions

View file

@ -2469,10 +2469,24 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TemplateLiteral::genera
return dst;
}
struct TagAndThisValue {
ScopedOperand tag;
ScopedOperand this_value;
};
Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TaggedTemplateLiteral::generate_bytecode(Bytecode::Generator& generator, Optional<ScopedOperand> preferred_dst) const
{
Bytecode::Generator::SourceLocationScope scope(generator, *this);
auto [tag, this_value] = TRY([&]() -> CodeGenerationErrorOr<TagAndThisValue> {
if (is<MemberExpression>(*m_tag)) {
auto& member_expression = static_cast<MemberExpression const&>(*m_tag);
auto base_and_value = TRY(get_base_and_value_from_member_expression(generator, member_expression));
return TagAndThisValue { .tag = base_and_value.value, .this_value = base_and_value.base };
}
auto tag = TRY(m_tag->generate_bytecode(generator)).value();
return TagAndThisValue { .tag = tag, .this_value = generator.add_constant(js_undefined()) };
}());
// FIXME: Follow
// 13.2.8.3 GetTemplateObject ( templateLiteral ), https://tc39.es/ecma262/#sec-gettemplateobject
@ -2539,7 +2553,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TaggedTemplateLiteral::
generator.emit<Bytecode::Op::NewArray>(arguments);
auto dst = choose_dst(generator, preferred_dst);
generator.emit<Bytecode::Op::CallWithArgumentArray>(Bytecode::Op::CallType::Call, dst, tag, generator.add_constant(js_undefined()), arguments);
generator.emit<Bytecode::Op::CallWithArgumentArray>(Bytecode::Op::CallType::Call, dst, tag, this_value, arguments);
return dst;
}

View file

@ -164,7 +164,7 @@ describe("tagged template literal functionality", () => {
expect(firstResult).toBe(secondResult);
});
test.xfail("this value of call comes from reference", () => {
test("this value of call comes from reference for non-computed, non-super property", () => {
let thisValue = null;
const obj = {
func() {
@ -176,4 +176,106 @@ describe("tagged template literal functionality", () => {
expect(thisValue).toBe(obj);
});
test("this value of call comes from reference for computed, non-super property", () => {
let thisValue = null;
const obj = {
func() {
thisValue = this;
},
};
obj["func"]``;
expect(thisValue).toBe(obj);
});
test("this value of call comes from reference for private property", () => {
let thisValue = null;
const obj = new (class A {
#func = () => {
thisValue = this;
};
constructor() {
this.#func``;
}
})();
expect(thisValue).toBe(obj);
});
test("this value of call comes from reference for non-computed super call property", () => {
let thisValue = null;
class A {
func() {
thisValue = this;
}
}
const obj = new (class B extends A {
constructor() {
super().func``;
}
})();
expect(thisValue).toBe(obj);
});
test("this value of call comes from reference for computed super call property", () => {
let thisValue = null;
class A {
func() {
thisValue = this;
}
}
const obj = new (class B extends A {
constructor() {
super()["func"]``;
}
})();
expect(thisValue).toBe(obj);
});
test("this value of call comes from reference for non-computed super property", () => {
let thisValue = null;
class A {
func() {
thisValue = this;
}
}
const obj = new (class B extends A {
constructor() {
super();
super.func``;
}
})();
expect(thisValue).toBe(obj);
});
test("this value of call comes from reference for computed super property", () => {
let thisValue = null;
class A {
func() {
thisValue = this;
}
}
const obj = new (class B extends A {
constructor() {
super();
super["func"]``;
}
})();
expect(thisValue).toBe(obj);
});
});