Skip to content

Commit 0dc9121

Browse files
committed
JS property accessors inlining (KT-13456)
1 parent 56ef09c commit 0dc9121

File tree

24 files changed

+719
-28
lines changed

24 files changed

+719
-28
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// PROPERTY_NOT_USED: p1
2+
// PROPERTY_NOT_READ_FROM: p2
3+
// PROPERTY_NOT_WRITTEN_TO: p3
4+
// CHECK_NOT_CALLED: get_p4
5+
// CHECK_NOT_CALLED: set_p4
6+
// CHECK_NOT_CALLED: get_p5
7+
// CHECK_NOT_CALLED: set_p6
8+
// PROPERTY_NOT_USED: p7
9+
// PROPERTY_NOT_READ_FROM: p8
10+
// PROPERTY_NOT_WRITTEN_TO: p9
11+
// CHECK_NOT_CALLED: get_p10_s8ev3o$
12+
// CHECK_NOT_CALLED: set_p10_rksjo2$
13+
// CHECK_NOT_CALLED: get_p11_s8ev3o$
14+
// CHECK_NOT_CALLED: set_p12_rksjo2$
15+
// CHECK_NOT_CALLED: get_p13
16+
// CHECK_NOT_CALLED: set_p13
17+
// CHECK_NOT_CALLED: get_p14
18+
// CHECK_NOT_CALLED: set_p15
19+
20+
// FILE: 1.kt
21+
package test
22+
23+
var a = 0
24+
25+
inline var p1: Int
26+
get() = a + 10000
27+
set(v: Int) {
28+
a = v + 100
29+
}
30+
31+
var p2: Int
32+
inline get() = a + 20000
33+
set(v: Int) {
34+
a = v + 200
35+
}
36+
37+
var p3: Int
38+
get() = a + 30000
39+
inline set(v: Int) {
40+
a = v + 300
41+
}
42+
43+
inline var Int.p4: Int
44+
get() = this * 100 + a + 40000
45+
set(v: Int) {
46+
a = this + v + 400
47+
}
48+
49+
var Int.p5: Int
50+
inline get() = this * 100 + a + 50000
51+
set(v: Int) {
52+
a = this + v + 500
53+
}
54+
55+
var Int.p6: Int
56+
get() = this * 100 + a + 60000
57+
inline set(v: Int) {
58+
a = this + v + 600
59+
}
60+
61+
class A {
62+
inline var p7: Int
63+
get() = a + 70000
64+
set(v: Int) {
65+
a = v + 700
66+
}
67+
68+
var p8: Int
69+
inline get() = a + 80000
70+
set(v: Int) {
71+
a = v + 800
72+
}
73+
74+
var p9: Int
75+
get() = a + 90000
76+
inline set(v: Int) {
77+
a = v + 900
78+
}
79+
80+
inline var Int.p10: Int
81+
get() = this * 100 + a + 100000
82+
set(v: Int) {
83+
a = this + v + 1000
84+
}
85+
86+
var Int.p11: Int
87+
inline get() = this * 100 + a + 110000
88+
set(v: Int) {
89+
a = this + v + 1100
90+
}
91+
92+
var Int.p12: Int
93+
get() = this * 100 + a + 120000
94+
inline set(v: Int) {
95+
a = this + v + 1200
96+
}
97+
}
98+
99+
inline var A.p13: Int
100+
get() = a + 130000
101+
set(v: Int) {
102+
a = v + 1300
103+
}
104+
105+
var A.p14: Int
106+
inline get() = a + 140000
107+
set(v: Int) {
108+
a = v + 1400
109+
}
110+
111+
var A.p15: Int
112+
get() = a + 150000
113+
inline set(v: Int) {
114+
a = v + 1500
115+
}
116+
117+
// FILE: 2.kt
118+
import test.*
119+
120+
fun box(): String {
121+
p1 = 1
122+
if (p1 != 10101) return "test1: $p1"
123+
p2 = 2
124+
if (p2 != 20202) return "test2: $p2"
125+
p3 = 3
126+
if (p3 != 30303) return "test3: $p3"
127+
128+
4000000.p4 = 4
129+
if (4000000.p4 != 404040404) return "test4: ${4000000.p4}"
130+
5000000.p5 = 5
131+
if (5000000.p5 != 505050505) return "test5: ${5000000.p5}"
132+
6000000.p6 = 6
133+
if (6000000.p6 != 606060606) return "test6: ${6000000.p6}"
134+
135+
136+
val a = A()
137+
138+
a.p7 = 7
139+
if (a.p7 != 70707) return "test7: ${a.p7}"
140+
a.p8 = 8
141+
if (a.p8 != 80808) return "test8: ${a.p8}"
142+
a.p9 = 9
143+
if (a.p9 != 90909) return "test9: ${a.p9}"
144+
145+
with (a) {
146+
10000000.p10 = 10
147+
if (10000000.p10 != 1010101010) return "test10: ${10000000.p10}"
148+
11000000.p11 = 11
149+
if (11000000.p11 != 1111111111) return "test11: ${11000000.p11}"
150+
12000000.p12 = 12
151+
if (12000000.p12 != 1212121212) return "test12: ${12000000.p12}"
152+
}
153+
154+
a.p13 = 13
155+
if (a.p13 != 131313) return "test13: ${a.p13}"
156+
a.p14 = 14
157+
if (a.p14 != 141414) return "test14: ${a.p14}"
158+
a.p15 = 15
159+
if (a.p15 != 151515) return "test15: ${a.p15}"
160+
161+
return "OK"
162+
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
// WITH_RUNTIME
2+
// WITH_REFLECT
23
// FILE: 1.kt
34
package test
45

56
inline val <reified T: Any> T.value: String
6-
get() = T::class.java.simpleName
7+
get() = T::class.simpleName!!
78

89
// FILE: 2.kt
910
import test.*
1011

1112
class OK
1213

1314
fun box(): String {
14-
return OK().value
15+
return OK().value ?: "fail"
1516
}

compiler/testData/codegen/boxInline/property/reifiedVar.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// WITH_RUNTIME
2+
// WITH_REFLECT
23
// FILE: 1.kt
34
package test
45

56
var bvalue: String = ""
67

78
inline var <reified T : Any> T.value: String
8-
get() = T::class.java.simpleName + bvalue
9+
get() = T::class.simpleName!! + bvalue
910
set(p: String) {
1011
bvalue = p
1112
}

compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,6 +1733,12 @@ public void testAllFilesPresentInProperty() throws Exception {
17331733
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/property"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
17341734
}
17351735

1736+
@TestMetadata("property.kt")
1737+
public void testProperty() throws Exception {
1738+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/property.kt");
1739+
doTest(fileName);
1740+
}
1741+
17361742
@TestMetadata("reifiedVal.kt")
17371743
public void testReifiedVal() throws Exception {
17381744
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/reifiedVal.kt");

compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,6 +1733,12 @@ public void testAllFilesPresentInProperty() throws Exception {
17331733
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/property"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
17341734
}
17351735

1736+
@TestMetadata("property.kt")
1737+
public void testProperty() throws Exception {
1738+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/property.kt");
1739+
doTest(fileName);
1740+
}
1741+
17361742
@TestMetadata("reifiedVal.kt")
17371743
public void testReifiedVal() throws Exception {
17381744
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/reifiedVal.kt");

generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ import org.jetbrains.kotlin.jps.incremental.AbstractProtoComparisonTest
133133
import org.jetbrains.kotlin.js.test.semantics.AbstractBoxJsTest
134134
import org.jetbrains.kotlin.js.test.semantics.AbstractJsCodegenBoxTest
135135
import org.jetbrains.kotlin.js.test.semantics.AbstractNonLocalReturnsTest
136+
import org.jetbrains.kotlin.js.test.semantics.AbstractPropertyAccessorsInlineTests
136137
import org.jetbrains.kotlin.jvm.compiler.*
137138
import org.jetbrains.kotlin.jvm.runtime.AbstractJvm8RuntimeDescriptorLoaderTest
138139
import org.jetbrains.kotlin.jvm.runtime.AbstractJvmRuntimeDescriptorLoaderTest
@@ -1188,6 +1189,10 @@ fun main(args: Array<String>) {
11881189
testClass<AbstractNonLocalReturnsTest> {
11891190
model("codegen/boxInline/nonLocalReturns/", targetBackend = TargetBackend.JS)
11901191
}
1192+
1193+
testClass<AbstractPropertyAccessorsInlineTests>() {
1194+
model("codegen/boxInline/property/", targetBackend = TargetBackend.JS)
1195+
}
11911196
}
11921197
}
11931198

js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ var JsInvocation.descriptor: CallableDescriptor? by MetadataProperty(default = n
3333

3434
var JsInvocation.psiElement: PsiElement? by MetadataProperty(default = null)
3535

36+
var JsNameRef.inlineStrategy: InlineStrategy? by MetadataProperty(default = null)
37+
38+
var JsNameRef.descriptor: CallableDescriptor? by MetadataProperty(default = null)
39+
40+
var JsNameRef.psiElement: PsiElement? by MetadataProperty(default = null)
41+
3642
var JsFunction.isLocal: Boolean by MetadataProperty(default = false)
3743

3844
var JsParameter.hasDefaultValue: Boolean by MetadataProperty(default = false)

js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.jetbrains.annotations.NotNull;
2424
import org.jetbrains.annotations.Nullable;
2525
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
26+
import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor;
27+
import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor;
2628
import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
2729
import org.jetbrains.kotlin.diagnostics.Errors;
2830
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor;
@@ -43,6 +45,7 @@
4345
public class JsInliner extends JsVisitorWithContextImpl {
4446

4547
private final Map<JsName, JsFunction> functions;
48+
private final Map<String, JsFunction> accessors;
4649
private final Stack<JsInliningContext> inliningContexts = new Stack<JsInliningContext>();
4750
private final Set<JsFunction> processedFunctions = CollectionUtilsKt.IdentitySet();
4851
private final Set<JsFunction> inProcessFunctions = CollectionUtilsKt.IdentitySet();
@@ -65,22 +68,94 @@ public Boolean invoke(JsNode node) {
6568
public static JsProgram process(@NotNull TranslationContext context) {
6669
JsProgram program = context.program();
6770
Map<JsName, JsFunction> functions = CollectUtilsKt.collectNamedFunctions(program);
68-
JsInliner inliner = new JsInliner(functions, new FunctionReader(context), context.bindingTrace());
71+
Map<String, JsFunction> accessors = CollectUtilsKt.collectAccessors(program);
72+
JsInliner inliner = new JsInliner(functions, accessors, new FunctionReader(context), context.bindingTrace());
6973
inliner.accept(program);
7074
RemoveUnusedFunctionDefinitionsKt.removeUnusedFunctionDefinitions(program, functions);
7175
return program;
7276
}
7377

7478
private JsInliner(
7579
@NotNull Map<JsName, JsFunction> functions,
80+
@NotNull Map<String, JsFunction> accessors,
7681
@NotNull FunctionReader functionReader,
7782
@NotNull DiagnosticSink trace
7883
) {
7984
this.functions = functions;
85+
this.accessors = accessors;
8086
this.functionReader = functionReader;
8187
this.trace = trace;
8288
}
8389

90+
@Override
91+
public boolean visit(@NotNull JsNameRef x, @NotNull JsContext ctx) {
92+
JsInvocation dummy = tryCreatePropertyGetterInvocation(x);
93+
if (dummy != null) {
94+
return visit(dummy, ctx);
95+
}
96+
return super.visit(x, ctx);
97+
}
98+
99+
@Override
100+
public void endVisit(@NotNull JsNameRef x, @NotNull JsContext ctx) {
101+
JsInvocation dummy = tryCreatePropertyGetterInvocation(x);
102+
if (dummy != null) {
103+
endVisit(dummy, ctx);
104+
}
105+
super.visit(x, ctx);
106+
}
107+
108+
@Override
109+
public boolean visit(@NotNull JsBinaryOperation x, @NotNull JsContext ctx) {
110+
JsInvocation dummy = tryCreatePropertySetterInvocation(x);
111+
if (dummy != null) {
112+
return visit(dummy, ctx);
113+
}
114+
return super.visit(x, ctx);
115+
}
116+
117+
@Override
118+
public void endVisit(@NotNull JsBinaryOperation x, @NotNull JsContext ctx) {
119+
JsInvocation dummy = tryCreatePropertySetterInvocation(x);
120+
if (dummy != null) {
121+
// Prevent FunctionInlineMutator from creating a variable for the result (there is none, because assignment is a statement)
122+
// TODO is there a better way to achieve this?
123+
getInliningContext().getStatementContext().replaceMe(new JsExpressionStatement(dummy));
124+
endVisit(dummy, ctx);
125+
}
126+
super.visit(x, ctx);
127+
}
128+
129+
@Nullable
130+
private static JsInvocation tryCreatePropertyGetterInvocation(@NotNull JsNameRef x) {
131+
if (MetadataProperties.getInlineStrategy(x) != null && MetadataProperties.getDescriptor(x) instanceof PropertyGetterDescriptor) {
132+
JsInvocation dummyInvocation = new JsInvocation(x);
133+
copyInlineMetadata(x, dummyInvocation);
134+
return dummyInvocation;
135+
}
136+
return null;
137+
}
138+
139+
@Nullable
140+
private static JsInvocation tryCreatePropertySetterInvocation(@NotNull JsBinaryOperation x) {
141+
if (!x.getOperator().isAssignment() || !(x.getArg1() instanceof JsNameRef)) return null;
142+
JsNameRef name = (JsNameRef) x.getArg1();
143+
if (MetadataProperties.getInlineStrategy(name) != null &&
144+
MetadataProperties.getDescriptor(name) instanceof PropertySetterDescriptor) {
145+
146+
JsInvocation dummyInvocation = new JsInvocation(name, x.getArg2());
147+
copyInlineMetadata(name, dummyInvocation);
148+
return dummyInvocation;
149+
}
150+
return null;
151+
}
152+
153+
private static void copyInlineMetadata(@NotNull JsNameRef from, @NotNull JsInvocation to) {
154+
MetadataProperties.setInlineStrategy(to, MetadataProperties.getInlineStrategy(from));
155+
MetadataProperties.setDescriptor(to, MetadataProperties.getDescriptor(from));
156+
MetadataProperties.setPsiElement(to, MetadataProperties.getPsiElement(from));
157+
}
158+
84159
@Override
85160
public boolean visit(@NotNull JsFunction function, @NotNull JsContext context) {
86161
inliningContexts.push(new JsInliningContext(function));
@@ -248,6 +323,12 @@ private class JsInliningContext implements InliningContext {
248323
protected JsFunction lookUpStaticFunction(@Nullable JsName functionName) {
249324
return functions.get(functionName);
250325
}
326+
327+
@Nullable
328+
@Override
329+
protected JsFunction lookUpStaticFunctionByTag(@NotNull String functionTag) {
330+
return accessors.get(functionTag);
331+
}
251332
};
252333
}
253334

0 commit comments

Comments
 (0)