Skip to content

Commit 6860b2c

Browse files
Alexey AndreevAlexey Andreev
authored andcommitted
JS: prevent generation of excessive updates of state fields
1 parent 4c0eb8f commit 6860b2c

File tree

4 files changed

+127
-8
lines changed

4 files changed

+127
-8
lines changed

js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineBodyTransformer.kt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,22 +123,28 @@ class CoroutineBodyTransformer(val program: JsProgram, val scope: JsScope, val t
123123
if (target != null) {
124124
val lhs = JsNameRef(stateFieldName, JsLiteral.THIS)
125125
val rhs = program.getNumberLiteral(blockIndexes[target]!!)
126-
ctx.replaceMe(JsAstUtils.assignment(lhs, rhs).makeStmt())
126+
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply {
127+
targetBlock = true
128+
})
127129
}
128130

129131
val exceptionTarget = x.targetExceptionBlock
130132
if (exceptionTarget != null) {
131133
val lhs = JsNameRef(exceptionStateName, JsLiteral.THIS)
132134
val rhs = program.getNumberLiteral(blockIndexes[exceptionTarget]!!)
133-
ctx.replaceMe(JsAstUtils.assignment(lhs, rhs).makeStmt())
135+
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply {
136+
targetExceptionBlock = true
137+
})
134138
}
135139

136140
val finallyPath = x.finallyPath
137141
if (finallyPath != null) {
138142
if (finallyPath.isNotEmpty()) {
139143
val lhs = JsNameRef(finallyPathFieldName, JsLiteral.THIS)
140144
val rhs = JsArrayLiteral(finallyPath.map { program.getNumberLiteral(blockIndexes[it]!!) })
141-
ctx.replaceMe(JsAstUtils.assignment(lhs, rhs).makeStmt())
145+
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs)).apply {
146+
this.finallyPath = true
147+
})
142148
}
143149
else {
144150
ctx.removeMe()
@@ -623,7 +629,3 @@ private fun CoroutineBlock.collectFinallyPaths(): List<List<CoroutineBlock>> {
623629
})
624630
return finallyPaths
625631
}
626-
627-
private var JsDebugger.targetBlock: CoroutineBlock? by MetadataProperty(default = null)
628-
private var JsDebugger.targetExceptionBlock: CoroutineBlock? by MetadataProperty(default = null)
629-
private var JsDebugger.finallyPath: List<CoroutineBlock>? by MetadataProperty(default = null)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2010-2016 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.js.coroutine
18+
19+
import com.google.dart.compiler.backend.js.ast.JsDebugger
20+
import com.google.dart.compiler.backend.js.ast.JsExpressionStatement
21+
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperty
22+
23+
var JsDebugger.targetBlock: CoroutineBlock? by MetadataProperty(default = null)
24+
var JsDebugger.targetExceptionBlock: CoroutineBlock? by MetadataProperty(default = null)
25+
var JsDebugger.finallyPath: List<CoroutineBlock>? by MetadataProperty(default = null)
26+
27+
var JsExpressionStatement.targetBlock by MetadataProperty(default = false)
28+
var JsExpressionStatement.targetExceptionBlock by MetadataProperty(default = false)
29+
var JsExpressionStatement.finallyPath by MetadataProperty(default = false)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2010-2016 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.js.inline.clean
18+
19+
import com.google.dart.compiler.backend.js.ast.*
20+
import org.jetbrains.kotlin.js.coroutine.finallyPath
21+
import org.jetbrains.kotlin.js.coroutine.targetBlock
22+
import org.jetbrains.kotlin.js.coroutine.targetExceptionBlock
23+
24+
class CoroutineStateElimination(private val body: JsBlock) {
25+
fun apply(): Boolean {
26+
var changed = false
27+
28+
body.accept(object : RecursiveJsVisitor() {
29+
override fun visitBlock(x: JsBlock) {
30+
visitStatements(x.statements)
31+
super.visitBlock(x)
32+
}
33+
34+
override fun visitCase(x: JsCase) {
35+
visitStatements(x.statements)
36+
super.visitCase(x)
37+
}
38+
39+
private fun visitStatements(statements: MutableList<JsStatement>) {
40+
class IndexHolder {
41+
var value: Int? = null
42+
}
43+
44+
val indexesToRemove = mutableSetOf<Int>()
45+
val lastTargetBlockIndex = IndexHolder()
46+
val lastTargetExceptionBlockIndex = IndexHolder()
47+
val lastFinallyPathIndex = IndexHolder()
48+
49+
for ((index, statement) in statements.withIndex()) {
50+
val indexesToUpdate = mutableListOf<IndexHolder>()
51+
if (statement is JsExpressionStatement) {
52+
if (statement.targetBlock) {
53+
indexesToUpdate += lastTargetBlockIndex
54+
}
55+
if (statement.targetExceptionBlock) {
56+
indexesToUpdate += lastTargetExceptionBlockIndex
57+
}
58+
if (statement.finallyPath) {
59+
indexesToUpdate += lastFinallyPathIndex
60+
}
61+
}
62+
63+
if (indexesToUpdate.isNotEmpty()) {
64+
for (indexToUpdate in indexesToUpdate) {
65+
indexToUpdate.value?.let { indexesToRemove += it }
66+
indexToUpdate.value = index
67+
}
68+
}
69+
else {
70+
lastTargetBlockIndex.value = null
71+
lastTargetExceptionBlockIndex.value = null
72+
lastFinallyPathIndex.value = null
73+
}
74+
}
75+
76+
for (index in indexesToRemove.sorted().reversed()) {
77+
statements.removeAt(index)
78+
}
79+
if (indexesToRemove.isNotEmpty()) {
80+
changed = true
81+
}
82+
}
83+
})
84+
85+
return changed
86+
}
87+
}

js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/FunctionPostProcessor.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class FunctionPostProcessor(root: JsFunction) {
2929
{ IfStatementReduction(root.body).apply() },
3030
{ DeadCodeElimination(root.body).apply() },
3131
{ RedundantVariableDeclarationElimination(root.body).apply() },
32-
{ RedundantStatementElimination(root).apply() }
32+
{ RedundantStatementElimination(root).apply() },
33+
{ CoroutineStateElimination(root.body).apply() }
3334
)
3435
// TODO: reduce to A || B, A && B if possible
3536

0 commit comments

Comments
 (0)