Skip to content

Commit 24e3544

Browse files
committed
[JSC] Tagged template with a super property tag should be called with the current this
https://bugs.webkit.org/show_bug.cgi?id=325756 Reviewed by Yusuke Suzuki. When the tag of a tagged template is a super property, JSC called the tag function with HomeObject.[[Prototype]] as `this`. Per spec, the this value of a super reference is the current `this` binding [1]. class A { tag() { return this; } } class B extends A { test() { return super.tag`x`; } } let b = new B; b.test() === b; // should be true, was false b.test() === A.prototype; // should be false, was true TaggedTemplateNode::emitBytecode passed the register used as the base of the property lookup as `this` of the call. This patch passes the current `this` instead, as FunctionCallDotNode and FunctionCallBracketNode already do. [1]: https://tc39.es/ecma262/#sec-getthisvalue Test: JSTests/stress/tagged-template-super-property-this.js * JSTests/stress/tagged-template-super-property-this.js: Added. (shouldBe): (shouldThrow): (A.prototype.tag): (A.tag): (A.prototype.get getter): (A.prototype.mark): (A): (B.prototype.dot): (B.prototype.bracket): (B.prototype.computed): (B.prototype.parenthesized): (B.prototype.withExpressions): (B.prototype.arrow): (B.prototype.viaEval): (B.prototype.getterDot): (B.prototype.getterBracket): (B.prototype.notTail): (B.prototype.markSelf): (B.dot): (B.bracket): (B): (C): (let.proto.tag): (let.object.dot): (let.object.bracket): (test): * Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp: (JSC::TaggedTemplateNode::emitBytecode): Canonical link: https://commits.webkit.org/322253@main
1 parent f63f4d8 commit 24e3544

2 files changed

Lines changed: 154 additions & 1 deletion

File tree

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
function shouldBe(actual, expected) {
2+
if (actual !== expected)
3+
throw new Error('bad value: ' + actual);
4+
}
5+
6+
function shouldThrow(func, errorType) {
7+
let error;
8+
try {
9+
func();
10+
} catch (e) {
11+
error = e;
12+
}
13+
if (!(error instanceof errorType))
14+
throw new Error('bad error: ' + error);
15+
}
16+
17+
let key = 'tag';
18+
19+
class A {
20+
tag() {
21+
return this;
22+
}
23+
static tag() {
24+
return this;
25+
}
26+
get getter() {
27+
let receiver = this;
28+
return function () {
29+
return [receiver, this];
30+
};
31+
}
32+
mark() {
33+
this.marked = true;
34+
}
35+
}
36+
37+
class B extends A {
38+
dot() {
39+
return super.tag`x`;
40+
}
41+
bracket() {
42+
return super['tag']`x`;
43+
}
44+
computed() {
45+
return super[key]`x`;
46+
}
47+
parenthesized() {
48+
return (super.tag)`x`;
49+
}
50+
withExpressions() {
51+
return super.tag`a${1}b${2}c`;
52+
}
53+
arrow() {
54+
return (() => super.tag`x`)();
55+
}
56+
viaEval() {
57+
return eval('super.tag`x`');
58+
}
59+
getterDot() {
60+
return super.getter`x`;
61+
}
62+
getterBracket() {
63+
return super['getter']`x`;
64+
}
65+
notTail() {
66+
let result = super.tag`x`;
67+
return result;
68+
}
69+
markSelf() {
70+
super.mark`x`;
71+
}
72+
static dot() {
73+
return super.tag`x`;
74+
}
75+
static bracket() {
76+
return super[key]`x`;
77+
}
78+
}
79+
80+
class C extends A {
81+
constructor(beforeSuper) {
82+
if (beforeSuper)
83+
super.tag`x`;
84+
super();
85+
this.dot = super.tag`x`;
86+
this.bracket = super[key]`x`;
87+
this.arrow = (() => super.tag`x`)();
88+
}
89+
}
90+
91+
let proto = {
92+
tag() {
93+
'use strict';
94+
return this;
95+
}
96+
};
97+
98+
let object = {
99+
__proto__: proto,
100+
dot() {
101+
return super.tag`x`;
102+
},
103+
bracket() {
104+
return super[key]`x`;
105+
}
106+
};
107+
108+
function test() {
109+
let b = new B;
110+
shouldBe(b.dot(), b);
111+
shouldBe(b.bracket(), b);
112+
shouldBe(b.computed(), b);
113+
shouldBe(b.parenthesized(), b);
114+
shouldBe(b.withExpressions(), b);
115+
shouldBe(b.arrow(), b);
116+
shouldBe(b.notTail(), b);
117+
118+
let [receiver, thisValue] = b.getterDot();
119+
shouldBe(receiver, b);
120+
shouldBe(thisValue, b);
121+
[receiver, thisValue] = b.getterBracket();
122+
shouldBe(receiver, b);
123+
shouldBe(thisValue, b);
124+
125+
b.markSelf();
126+
shouldBe(b.hasOwnProperty('marked'), true);
127+
shouldBe(A.prototype.hasOwnProperty('marked'), false);
128+
129+
shouldBe(B.dot(), B);
130+
shouldBe(B.bracket(), B);
131+
132+
shouldBe(B.prototype.dot.call(1), 1);
133+
shouldBe(B.prototype.bracket.call(undefined), undefined);
134+
135+
let c = new C(false);
136+
shouldBe(c.dot, c);
137+
shouldBe(c.bracket, c);
138+
shouldBe(c.arrow, c);
139+
140+
shouldBe(object.dot(), object);
141+
shouldBe(object.bracket(), object);
142+
}
143+
144+
for (let i = 0; i < testLoopCount; i++)
145+
test();
146+
147+
let b = new B;
148+
shouldBe(b.viaEval(), b);
149+
shouldThrow(() => new C(true), ReferenceError);

‎Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ RegisterID* TaggedTemplateNode::emitBytecode(BytecodeGenerator& generator, Regis
401401
if (bracket->base()->isSuperNode()) {
402402
RefPtr<RegisterID> thisValue = generator.ensureThis();
403403
tag = generator.emitGetByVal(generator.newTemporary(), base.get(), thisValue.get(), property.get());
404+
base = WTF::move(thisValue);
404405
} else
405406
tag = generator.emitGetByVal(generator.newTemporary(), base.get(), property.get());
406407
} else {
@@ -409,7 +410,10 @@ RegisterID* TaggedTemplateNode::emitBytecode(BytecodeGenerator& generator, Regis
409410
tag = generator.newTemporary();
410411
base = generator.newTemporary();
411412
base = generator.emitNode(base.get(), dot->base());
412-
tag = dot->emitGetPropertyValue(generator, tag.get(), base.get());
413+
RefPtr<RegisterID> thisValue;
414+
tag = dot->emitGetPropertyValue(generator, tag.get(), base.get(), thisValue);
415+
if (thisValue)
416+
base = WTF::move(thisValue);
413417
}
414418

415419
RefPtr<RegisterID> templateObject = generator.emitGetTemplateObject(nullptr, this);

0 commit comments

Comments
 (0)