Minor: reformat code in org.jetbrains.kotlin.codegen.optimization
This commit is contained in:
+24
-24
@@ -46,10 +46,10 @@ class ApiVersionCallsPreprocessingMethodTransformer(private val targetApiVersion
|
|||||||
val atLeastVersion = MavenComparableVersion("$epic.$major.$minor")
|
val atLeastVersion = MavenComparableVersion("$epic.$major.$minor")
|
||||||
|
|
||||||
val replacementInsn =
|
val replacementInsn =
|
||||||
if (targetApiVersion.version >= atLeastVersion)
|
if (targetApiVersion.version >= atLeastVersion)
|
||||||
InsnNode(Opcodes.ICONST_1)
|
InsnNode(Opcodes.ICONST_1)
|
||||||
else
|
else
|
||||||
InsnNode(Opcodes.ICONST_0)
|
InsnNode(Opcodes.ICONST_0)
|
||||||
|
|
||||||
methodNode.instructions.run {
|
methodNode.instructions.run {
|
||||||
remove(prev1)
|
remove(prev1)
|
||||||
@@ -65,29 +65,29 @@ class ApiVersionCallsPreprocessingMethodTransformer(private val targetApiVersion
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun AbstractInsnNode.isApiVersionIsAtLeastCall(): Boolean =
|
private fun AbstractInsnNode.isApiVersionIsAtLeastCall(): Boolean =
|
||||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||||
owner.startsWith("kotlin/internal") &&
|
owner.startsWith("kotlin/internal") &&
|
||||||
name == "apiVersionIsAtLeast" &&
|
name == "apiVersionIsAtLeast" &&
|
||||||
desc == "(III)Z"
|
desc == "(III)Z"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AbstractInsnNode.getIntConstValue(): Int? =
|
private fun AbstractInsnNode.getIntConstValue(): Int? =
|
||||||
when (this) {
|
when (this) {
|
||||||
is InsnNode ->
|
is InsnNode ->
|
||||||
if (opcode in Opcodes.ICONST_M1..Opcodes.ICONST_5)
|
if (opcode in Opcodes.ICONST_M1..Opcodes.ICONST_5)
|
||||||
opcode - Opcodes.ICONST_0
|
opcode - Opcodes.ICONST_0
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
|
|
||||||
is IntInsnNode ->
|
is IntInsnNode ->
|
||||||
when (opcode) {
|
when (opcode) {
|
||||||
Opcodes.BIPUSH -> operand
|
Opcodes.BIPUSH -> operand
|
||||||
Opcodes.SIPUSH -> operand
|
Opcodes.SIPUSH -> operand
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
is LdcInsnNode -> cst as? Int
|
is LdcInsnNode -> cst as? Int
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+21
-27
@@ -17,13 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.codegen.optimization
|
package org.jetbrains.kotlin.codegen.optimization
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.ProperTrackedReferenceValue
|
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.ReferenceTrackingInterpreter
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.ReferenceValueDescriptor
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.TrackedReferenceValue
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
@@ -71,10 +65,10 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
var cleanVarInstruction: VarInsnNode? = null
|
var cleanVarInstruction: VarInsnNode? = null
|
||||||
|
|
||||||
fun canRewrite(): Boolean =
|
fun canRewrite(): Boolean =
|
||||||
!hazard &&
|
!hazard &&
|
||||||
initCallInsn != null &&
|
initCallInsn != null &&
|
||||||
localVar != null &&
|
localVar != null &&
|
||||||
localVarIndex >= 0
|
localVarIndex >= 0
|
||||||
|
|
||||||
override fun onUseAsTainted() {
|
override fun onUseAsTainted() {
|
||||||
hazard = true
|
hazard = true
|
||||||
@@ -118,10 +112,10 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
|
|
||||||
private inner class Interpreter : ReferenceTrackingInterpreter() {
|
private inner class Interpreter : ReferenceTrackingInterpreter() {
|
||||||
override fun newOperation(insn: AbstractInsnNode): BasicValue =
|
override fun newOperation(insn: AbstractInsnNode): BasicValue =
|
||||||
refValuesByNewInsn[insn]?.let { descriptor ->
|
refValuesByNewInsn[insn]?.let { descriptor ->
|
||||||
ProperTrackedReferenceValue(descriptor.refType, descriptor)
|
ProperTrackedReferenceValue(descriptor.refType, descriptor)
|
||||||
}
|
}
|
||||||
?: super.newOperation(insn)
|
?: super.newOperation(insn)
|
||||||
|
|
||||||
override fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int) {
|
override fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int) {
|
||||||
for (descriptor in value.descriptors) {
|
for (descriptor in value.descriptors) {
|
||||||
@@ -179,7 +173,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun BasicValue.getCapturedVarOrNull() =
|
private fun BasicValue.getCapturedVarOrNull() =
|
||||||
safeAs<ProperTrackedReferenceValue>()?.descriptor?.safeAs<CapturedVarDescriptor>()
|
safeAs<ProperTrackedReferenceValue>()?.descriptor?.safeAs<CapturedVarDescriptor>()
|
||||||
|
|
||||||
private fun assignLocalVars() {
|
private fun assignLocalVars() {
|
||||||
for (localVar in methodNode.localVariables) {
|
for (localVar in methodNode.localVariables) {
|
||||||
@@ -195,8 +189,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
|
|
||||||
if (descriptor.localVar == null) {
|
if (descriptor.localVar == null) {
|
||||||
descriptor.localVar = localVar
|
descriptor.localVar = localVar
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
descriptor.hazard = true
|
descriptor.hazard = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,8 +203,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
refValue.localVarIndex = methodNode.maxLocals
|
refValue.localVarIndex = methodNode.maxLocals
|
||||||
methodNode.maxLocals += 2
|
methodNode.maxLocals += 2
|
||||||
localVar.index = refValue.localVarIndex
|
localVar.index = refValue.localVarIndex
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
refValue.localVarIndex = localVar.index
|
refValue.localVarIndex = localVar.index
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +215,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val cleanInstructions = findCleanInstructions(refValue, oldVarIndex, methodNode.instructions)
|
val cleanInstructions = findCleanInstructions(refValue, oldVarIndex, methodNode.instructions)
|
||||||
if (cleanInstructions.size > 1 ) {
|
if (cleanInstructions.size > 1) {
|
||||||
refValue.hazard = true
|
refValue.hazard = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -235,12 +227,14 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
return InsnSequence(instructions).filterIsInstance<VarInsnNode>().filter {
|
return InsnSequence(instructions).filterIsInstance<VarInsnNode>().filter {
|
||||||
it.opcode == Opcodes.ASTORE && it.`var` == oldVarIndex
|
it.opcode == Opcodes.ASTORE && it.`var` == oldVarIndex
|
||||||
}.filter {
|
}.filter {
|
||||||
it.previous?.opcode == Opcodes.ACONST_NULL
|
it.previous?.opcode == Opcodes.ACONST_NULL
|
||||||
}.filter {
|
}.filter {
|
||||||
val operationIndex = instructions.indexOf(it)
|
val operationIndex = instructions.indexOf(it)
|
||||||
val localVariableNode = refValue.localVar!!
|
val localVariableNode = refValue.localVar!!
|
||||||
instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf(localVariableNode.end)
|
instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf(
|
||||||
}.toList()
|
localVariableNode.end
|
||||||
|
)
|
||||||
|
}.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rewrite() {
|
private fun rewrite() {
|
||||||
|
|||||||
+36
-37
@@ -48,20 +48,20 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun collectRewriteActions(): List<() -> Unit> =
|
private fun collectRewriteActions(): List<() -> Unit> =
|
||||||
arrayListOf<() -> Unit>().also { actions ->
|
arrayListOf<() -> Unit>().also { actions ->
|
||||||
val frames = analyze(internalClassName, methodNode, ConstantPropagationInterpreter())
|
val frames = analyze(internalClassName, methodNode, ConstantPropagationInterpreter())
|
||||||
val insns = methodNode.instructions.toArray()
|
val insns = methodNode.instructions.toArray()
|
||||||
for (i in frames.indices) {
|
for (i in frames.indices) {
|
||||||
val frame = frames[i] ?: continue
|
val frame = frames[i] ?: continue
|
||||||
val insn = insns[i] as? JumpInsnNode ?: continue
|
val insn = insns[i] as? JumpInsnNode ?: continue
|
||||||
when (insn.opcode) {
|
when (insn.opcode) {
|
||||||
in Opcodes.IFEQ .. Opcodes.IFLE ->
|
in Opcodes.IFEQ..Opcodes.IFLE ->
|
||||||
tryRewriteComparisonWithZero(insn, frame, actions)
|
tryRewriteComparisonWithZero(insn, frame, actions)
|
||||||
in Opcodes.IF_ICMPEQ .. Opcodes.IF_ICMPLE ->
|
in Opcodes.IF_ICMPEQ..Opcodes.IF_ICMPLE ->
|
||||||
tryRewriteBinaryComparison(insn, frame, actions)
|
tryRewriteBinaryComparison(insn, frame, actions)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun tryRewriteComparisonWithZero(insn: JumpInsnNode, frame: Frame<BasicValue>, actions: ArrayList<() -> Unit>) {
|
private fun tryRewriteComparisonWithZero(insn: JumpInsnNode, frame: Frame<BasicValue>, actions: ArrayList<() -> Unit>) {
|
||||||
val top = frame.top()!!.safeAs<IConstValue>() ?: return
|
val top = frame.top()!!.safeAs<IConstValue>() ?: return
|
||||||
@@ -93,8 +93,7 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
|
|||||||
|
|
||||||
if (arg1 is IConstValue && arg2 is IConstValue) {
|
if (arg1 is IConstValue && arg2 is IConstValue) {
|
||||||
rewriteBinaryComparisonOfConsts(insn, arg1.value, arg2.value, actions)
|
rewriteBinaryComparisonOfConsts(insn, arg1.value, arg2.value, actions)
|
||||||
}
|
} else if (arg2 is IConstValue && arg2.value == 0) {
|
||||||
else if (arg2 is IConstValue && arg2.value == 0) {
|
|
||||||
rewriteBinaryComparisonWith0(insn, actions)
|
rewriteBinaryComparisonWith0(insn, actions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,8 +143,8 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
|
|||||||
|
|
||||||
private class IConstValue private constructor(val value: Int) : StrictBasicValue(Type.INT_TYPE) {
|
private class IConstValue private constructor(val value: Int) : StrictBasicValue(Type.INT_TYPE) {
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
other === this ||
|
other === this ||
|
||||||
other is IConstValue && other.value == this.value
|
other is IConstValue && other.value == this.value
|
||||||
|
|
||||||
override fun hashCode(): Int = value
|
override fun hashCode(): Int = value
|
||||||
|
|
||||||
@@ -155,34 +154,34 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
|
|||||||
private val ICONST_CACHE = Array(7) { IConstValue(it - 1) }
|
private val ICONST_CACHE = Array(7) { IConstValue(it - 1) }
|
||||||
|
|
||||||
fun of(value: Int) =
|
fun of(value: Int) =
|
||||||
if (value in -1 .. 5)
|
if (value in -1..5)
|
||||||
ICONST_CACHE[value + 1]
|
ICONST_CACHE[value + 1]
|
||||||
else
|
else
|
||||||
IConstValue(value)
|
IConstValue(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ConstantPropagationInterpreter : OptimizationBasicInterpreter() {
|
private class ConstantPropagationInterpreter : OptimizationBasicInterpreter() {
|
||||||
override fun newOperation(insn: AbstractInsnNode): BasicValue =
|
override fun newOperation(insn: AbstractInsnNode): BasicValue =
|
||||||
when (insn.opcode) {
|
when (insn.opcode) {
|
||||||
in Opcodes.ICONST_M1 .. Opcodes.ICONST_5 ->
|
in Opcodes.ICONST_M1..Opcodes.ICONST_5 ->
|
||||||
IConstValue.of(insn.opcode - Opcodes.ICONST_0)
|
IConstValue.of(insn.opcode - Opcodes.ICONST_0)
|
||||||
Opcodes.BIPUSH, Opcodes.SIPUSH ->
|
Opcodes.BIPUSH, Opcodes.SIPUSH ->
|
||||||
IConstValue.of(insn.cast<IntInsnNode>().operand)
|
IConstValue.of(insn.cast<IntInsnNode>().operand)
|
||||||
Opcodes.LDC -> {
|
Opcodes.LDC -> {
|
||||||
val operand = insn.cast<LdcInsnNode>().cst
|
val operand = insn.cast<LdcInsnNode>().cst
|
||||||
if (operand is Int)
|
if (operand is Int)
|
||||||
IConstValue.of(operand)
|
IConstValue.of(operand)
|
||||||
else
|
else
|
||||||
super.newOperation(insn)
|
super.newOperation(insn)
|
||||||
}
|
|
||||||
else -> super.newOperation(insn)
|
|
||||||
}
|
}
|
||||||
|
else -> super.newOperation(insn)
|
||||||
|
}
|
||||||
|
|
||||||
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
||||||
if (v is IConstValue && w is IConstValue && v == w)
|
if (v is IConstValue && w is IConstValue && v == w)
|
||||||
v
|
v
|
||||||
else
|
else
|
||||||
super.merge(v, w)
|
super.merge(v, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
-9
@@ -55,15 +55,15 @@ class DeadCodeEliminationMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldRemove(insn: AbstractInsnNode, index: Int, frames: Array<out Any?>): Boolean =
|
private fun shouldRemove(insn: AbstractInsnNode, index: Int, frames: Array<out Any?>): Boolean =
|
||||||
when (insn) {
|
when (insn) {
|
||||||
is LabelNode ->
|
is LabelNode ->
|
||||||
// Do not remove label nodes because they can be referred by try/catch blocks or local variables table
|
// Do not remove label nodes because they can be referred by try/catch blocks or local variables table
|
||||||
false
|
false
|
||||||
is LineNumberNode ->
|
is LineNumberNode ->
|
||||||
isDeadLineNumber(insn, index, frames)
|
isDeadLineNumber(insn, index, frames)
|
||||||
else ->
|
else ->
|
||||||
frames[index] == null
|
frames[index] == null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isDeadLineNumber(insn: LineNumberNode, index: Int, frames: Array<out Any?>): Boolean {
|
private fun isDeadLineNumber(insn: LineNumberNode, index: Int, frames: Array<out Any?>): Boolean {
|
||||||
// Line number node is "dead" if the corresponding line number interval
|
// Line number node is "dead" if the corresponding line number interval
|
||||||
|
|||||||
+2
-4
@@ -18,10 +18,8 @@ package org.jetbrains.kotlin.codegen.optimization
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
|
||||||
|
|
||||||
class FixStackWithLabelNormalizationMethodTransformer : CompositeMethodTransformer(
|
class FixStackWithLabelNormalizationMethodTransformer : CompositeMethodTransformer(
|
||||||
LabelNormalizationMethodTransformer(),
|
LabelNormalizationMethodTransformer(),
|
||||||
FixStackMethodTransformer()
|
FixStackMethodTransformer()
|
||||||
)
|
)
|
||||||
+17
-19
@@ -49,13 +49,11 @@ class LabelNormalizationMethodTransformer : MethodTransformer() {
|
|||||||
newLabelNodes[thisNode.label] = prevNode
|
newLabelNodes[thisNode.label] = prevNode
|
||||||
removedAnyLabels = true
|
removedAnyLabels = true
|
||||||
thisNode = instructions.removeNodeGetNext(thisNode)
|
thisNode = instructions.removeNodeGetNext(thisNode)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
newLabelNodes[thisNode.label] = thisNode
|
newLabelNodes[thisNode.label] = thisNode
|
||||||
thisNode = thisNode.next
|
thisNode = thisNode.next
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
thisNode = thisNode.next
|
thisNode = thisNode.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,19 +81,19 @@ class LabelNormalizationMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun rewriteLineNumberNode(oldLineNode: LineNumberNode): AbstractInsnNode? =
|
private fun rewriteLineNumberNode(oldLineNode: LineNumberNode): AbstractInsnNode? =
|
||||||
instructions.replaceNodeGetNext(oldLineNode, oldLineNode.rewriteLabels())
|
instructions.replaceNodeGetNext(oldLineNode, oldLineNode.rewriteLabels())
|
||||||
|
|
||||||
private fun rewriteJumpInsn(oldJumpNode: JumpInsnNode): AbstractInsnNode? =
|
private fun rewriteJumpInsn(oldJumpNode: JumpInsnNode): AbstractInsnNode? =
|
||||||
instructions.replaceNodeGetNext(oldJumpNode, oldJumpNode.rewriteLabels())
|
instructions.replaceNodeGetNext(oldJumpNode, oldJumpNode.rewriteLabels())
|
||||||
|
|
||||||
private fun rewriteLookupSwitchInsn(oldSwitchNode: LookupSwitchInsnNode): AbstractInsnNode? =
|
private fun rewriteLookupSwitchInsn(oldSwitchNode: LookupSwitchInsnNode): AbstractInsnNode? =
|
||||||
instructions.replaceNodeGetNext(oldSwitchNode, oldSwitchNode.rewriteLabels())
|
instructions.replaceNodeGetNext(oldSwitchNode, oldSwitchNode.rewriteLabels())
|
||||||
|
|
||||||
private fun rewriteTableSwitchInsn(oldSwitchNode: TableSwitchInsnNode): AbstractInsnNode? =
|
private fun rewriteTableSwitchInsn(oldSwitchNode: TableSwitchInsnNode): AbstractInsnNode? =
|
||||||
instructions.replaceNodeGetNext(oldSwitchNode, oldSwitchNode.rewriteLabels())
|
instructions.replaceNodeGetNext(oldSwitchNode, oldSwitchNode.rewriteLabels())
|
||||||
|
|
||||||
private fun rewriteFrameNode(oldFrameNode: FrameNode): AbstractInsnNode? =
|
private fun rewriteFrameNode(oldFrameNode: FrameNode): AbstractInsnNode? =
|
||||||
instructions.replaceNodeGetNext(oldFrameNode, oldFrameNode.rewriteLabels())
|
instructions.replaceNodeGetNext(oldFrameNode, oldFrameNode.rewriteLabels())
|
||||||
|
|
||||||
private fun rewriteTryCatchBlocks() {
|
private fun rewriteTryCatchBlocks() {
|
||||||
methodNode.tryCatchBlocks = methodNode.tryCatchBlocks.map { oldTcb ->
|
methodNode.tryCatchBlocks = methodNode.tryCatchBlocks.map { oldTcb ->
|
||||||
@@ -109,21 +107,21 @@ class LabelNormalizationMethodTransformer : MethodTransformer() {
|
|||||||
private fun rewriteLocalVars() {
|
private fun rewriteLocalVars() {
|
||||||
methodNode.localVariables = methodNode.localVariables.map { oldVar ->
|
methodNode.localVariables = methodNode.localVariables.map { oldVar ->
|
||||||
LocalVariableNode(
|
LocalVariableNode(
|
||||||
oldVar.name,
|
oldVar.name,
|
||||||
oldVar.desc,
|
oldVar.desc,
|
||||||
oldVar.signature,
|
oldVar.signature,
|
||||||
getNew(oldVar.start),
|
getNew(oldVar.start),
|
||||||
getNew(oldVar.end),
|
getNew(oldVar.end),
|
||||||
oldVar.index
|
oldVar.index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LineNumberNode.rewriteLabels(): AbstractInsnNode =
|
private fun LineNumberNode.rewriteLabels(): AbstractInsnNode =
|
||||||
LineNumberNode(line, getNewOrOld(start))
|
LineNumberNode(line, getNewOrOld(start))
|
||||||
|
|
||||||
private fun JumpInsnNode.rewriteLabels(): AbstractInsnNode =
|
private fun JumpInsnNode.rewriteLabels(): AbstractInsnNode =
|
||||||
JumpInsnNode(opcode, getNew(label))
|
JumpInsnNode(opcode, getNew(label))
|
||||||
|
|
||||||
private fun LookupSwitchInsnNode.rewriteLabels(): AbstractInsnNode {
|
private fun LookupSwitchInsnNode.rewriteLabels(): AbstractInsnNode {
|
||||||
val switchNode = LookupSwitchInsnNode(getNew(dflt), keys.toIntArray(), emptyArray())
|
val switchNode = LookupSwitchInsnNode(getNew(dflt), keys.toIntArray(), emptyArray())
|
||||||
@@ -145,10 +143,10 @@ class LabelNormalizationMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getNew(oldLabelNode: LabelNode): LabelNode =
|
private fun getNew(oldLabelNode: LabelNode): LabelNode =
|
||||||
newLabelNodes[oldLabelNode.label]!!
|
newLabelNodes[oldLabelNode.label]!!
|
||||||
|
|
||||||
private fun getNewOrOld(oldLabelNode: LabelNode): LabelNode =
|
private fun getNewOrOld(oldLabelNode: LabelNode): LabelNode =
|
||||||
newLabelNodes[oldLabelNode.label] ?: oldLabelNode
|
newLabelNodes[oldLabelNode.label] ?: oldLabelNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ class MethodVerifier(private val checkPoint: String) : MethodTransformer() {
|
|||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
try {
|
try {
|
||||||
analyze(internalClassName, methodNode, BasicVerifier())
|
analyze(internalClassName, methodNode, BasicVerifier())
|
||||||
}
|
} catch (e: Throwable) {
|
||||||
catch (e: Throwable) {
|
|
||||||
throw AssertionError("$checkPoint: incorrect bytecode", e)
|
throw AssertionError("$checkPoint: incorrect bytecode", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-22
@@ -28,17 +28,17 @@ import org.jetbrains.org.objectweb.asm.MethodVisitor
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
class OptimizationMethodVisitor(
|
class OptimizationMethodVisitor(
|
||||||
delegate: MethodVisitor,
|
delegate: MethodVisitor,
|
||||||
private val disableOptimization: Boolean,
|
private val disableOptimization: Boolean,
|
||||||
private val constructorCallNormalizationMode: JVMConstructorCallNormalizationMode,
|
private val constructorCallNormalizationMode: JVMConstructorCallNormalizationMode,
|
||||||
access: Int,
|
access: Int,
|
||||||
name: String,
|
name: String,
|
||||||
desc: String,
|
desc: String,
|
||||||
signature: String?,
|
signature: String?,
|
||||||
exceptions: Array<String>?
|
exceptions: Array<String>?
|
||||||
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
|
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
|
||||||
private val constructorCallNormalizationTransformer =
|
private val constructorCallNormalizationTransformer =
|
||||||
UninitializedStoresMethodTransformer(constructorCallNormalizationMode)
|
UninitializedStoresMethodTransformer(constructorCallNormalizationMode)
|
||||||
|
|
||||||
override fun performTransformations(methodNode: MethodNode) {
|
override fun performTransformations(methodNode: MethodNode) {
|
||||||
normalizationMethodTransformer.transform("fake", methodNode)
|
normalizationMethodTransformer.transform("fake", methodNode)
|
||||||
@@ -55,22 +55,22 @@ class OptimizationMethodVisitor(
|
|||||||
private val MEMORY_LIMIT_BY_METHOD_MB = 50
|
private val MEMORY_LIMIT_BY_METHOD_MB = 50
|
||||||
|
|
||||||
val normalizationMethodTransformer = CompositeMethodTransformer(
|
val normalizationMethodTransformer = CompositeMethodTransformer(
|
||||||
FixStackWithLabelNormalizationMethodTransformer(),
|
FixStackWithLabelNormalizationMethodTransformer(),
|
||||||
MethodVerifier("AFTER mandatory stack transformations")
|
MethodVerifier("AFTER mandatory stack transformations")
|
||||||
)
|
)
|
||||||
|
|
||||||
val optimizationTransformer = CompositeMethodTransformer(
|
val optimizationTransformer = CompositeMethodTransformer(
|
||||||
CapturedVarsOptimizationMethodTransformer(),
|
CapturedVarsOptimizationMethodTransformer(),
|
||||||
RedundantNullCheckMethodTransformer(),
|
RedundantNullCheckMethodTransformer(),
|
||||||
RedundantCheckCastEliminationMethodTransformer(),
|
RedundantCheckCastEliminationMethodTransformer(),
|
||||||
ConstantConditionEliminationMethodTransformer(),
|
ConstantConditionEliminationMethodTransformer(),
|
||||||
RedundantBoxingMethodTransformer(),
|
RedundantBoxingMethodTransformer(),
|
||||||
StackPeepholeOptimizationsTransformer(),
|
StackPeepholeOptimizationsTransformer(),
|
||||||
PopBackwardPropagationTransformer(),
|
PopBackwardPropagationTransformer(),
|
||||||
DeadCodeEliminationMethodTransformer(),
|
DeadCodeEliminationMethodTransformer(),
|
||||||
RedundantGotoMethodTransformer(),
|
RedundantGotoMethodTransformer(),
|
||||||
RedundantNopsCleanupMethodTransformer(),
|
RedundantNopsCleanupMethodTransformer(),
|
||||||
MethodVerifier("AFTER optimizations")
|
MethodVerifier("AFTER optimizations")
|
||||||
)
|
)
|
||||||
|
|
||||||
fun canBeOptimized(node: MethodNode): Boolean {
|
fun canBeOptimized(node: MethodNode): Boolean {
|
||||||
|
|||||||
+3
-2
@@ -22,7 +22,8 @@ import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
||||||
|
|
||||||
class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
|
class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
|
||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
@@ -57,7 +58,7 @@ class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isTrivialSubtype(superType: Type, subType: Type) =
|
private fun isTrivialSubtype(superType: Type, subType: Type) =
|
||||||
superType == subType
|
superType == subType
|
||||||
|
|
||||||
private fun isMultiArrayType(type: Type) = type.sort == Type.ARRAY && type.dimensions != 1
|
private fun isMultiArrayType(type: Type) = type.sort == Type.ARRAY && type.dimensions != 1
|
||||||
}
|
}
|
||||||
+7
-7
@@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.optimization
|
package org.jetbrains.kotlin.codegen.optimization
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
class RedundantGotoMethodTransformer : MethodTransformer() {
|
class RedundantGotoMethodTransformer : MethodTransformer() {
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +38,7 @@ class RedundantGotoMethodTransformer : MethodTransformer() {
|
|||||||
insn is LabelNode ->
|
insn is LabelNode ->
|
||||||
currentLabels.add(insn)
|
currentLabels.add(insn)
|
||||||
insn.opcode == Opcodes.GOTO &&
|
insn.opcode == Opcodes.GOTO &&
|
||||||
(insn as JumpInsnNode).label in currentLabels ->
|
(insn as JumpInsnNode).label in currentLabels ->
|
||||||
insnsToRemove.add(insn)
|
insnsToRemove.add(insn)
|
||||||
insn.isMeaningful ->
|
insn.isMeaningful ->
|
||||||
currentLabels.clear()
|
currentLabels.clear()
|
||||||
|
|||||||
+3
-5
@@ -46,8 +46,7 @@ class RedundantNopsCleanupMethodTransformer : MethodTransformer() {
|
|||||||
val toRemove = current
|
val toRemove = current
|
||||||
current = current.next
|
current = current.next
|
||||||
methodNode.instructions.remove(toRemove)
|
methodNode.instructions.remove(toRemove)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
current = current.next
|
current = current.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +83,7 @@ class RedundantNopsCleanupMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (i in 0 .. localVariableLabels.size - 2) {
|
for (i in 0..localVariableLabels.size - 2) {
|
||||||
val begin = localVariableLabels[i]
|
val begin = localVariableLabels[i]
|
||||||
val end = localVariableLabels[i + 1]
|
val end = localVariableLabels[i + 1]
|
||||||
if (InsnSequence(begin, end).any { it in requiredNops }) continue
|
if (InsnSequence(begin, end).any { it in requiredNops }) continue
|
||||||
@@ -109,8 +108,7 @@ internal fun getRequiredNopInRange(firstInclusive: AbstractInsnNode, lastExclusi
|
|||||||
while (current != null && current != lastExclusive) {
|
while (current != null && current != lastExclusive) {
|
||||||
if (current.isMeaningful && current.opcode != Opcodes.NOP) {
|
if (current.isMeaningful && current.opcode != Opcodes.NOP) {
|
||||||
return null
|
return null
|
||||||
}
|
} else if (current.opcode == Opcodes.NOP) {
|
||||||
else if (current.opcode == Opcodes.NOP) {
|
|
||||||
lastNop = current
|
lastNop = current
|
||||||
}
|
}
|
||||||
current = current.next
|
current = current.next
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
class UninitializedStoresMethodTransformer(
|
class UninitializedStoresMethodTransformer(
|
||||||
private val mode: JVMConstructorCallNormalizationMode
|
private val mode: JVMConstructorCallNormalizationMode
|
||||||
) : MethodTransformer() {
|
) : MethodTransformer() {
|
||||||
|
|
||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
|
|||||||
+9
-9
@@ -34,9 +34,9 @@ abstract class BoxedBasicValue(type: Type) : StrictBasicValue(type) {
|
|||||||
|
|
||||||
|
|
||||||
class CleanBoxedValue(
|
class CleanBoxedValue(
|
||||||
boxedType: Type,
|
boxedType: Type,
|
||||||
boxingInsn: AbstractInsnNode,
|
boxingInsn: AbstractInsnNode,
|
||||||
progressionIterator: ProgressionIteratorBasicValue?
|
progressionIterator: ProgressionIteratorBasicValue?
|
||||||
) : BoxedBasicValue(boxedType) {
|
) : BoxedBasicValue(boxedType) {
|
||||||
override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator)
|
override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator)
|
||||||
|
|
||||||
@@ -53,9 +53,9 @@ class TaintedBoxedValue(private val boxedBasicValue: CleanBoxedValue) : BoxedBas
|
|||||||
|
|
||||||
|
|
||||||
class BoxedValueDescriptor(
|
class BoxedValueDescriptor(
|
||||||
private val boxedType: Type,
|
private val boxedType: Type,
|
||||||
val boxingInsn: AbstractInsnNode,
|
val boxingInsn: AbstractInsnNode,
|
||||||
val progressionIterator: ProgressionIteratorBasicValue?
|
val progressionIterator: ProgressionIteratorBasicValue?
|
||||||
) {
|
) {
|
||||||
private val associatedInsns = HashSet<AbstractInsnNode>()
|
private val associatedInsns = HashSet<AbstractInsnNode>()
|
||||||
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
|
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
|
||||||
@@ -76,14 +76,14 @@ class BoxedValueDescriptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getVariablesIndexes(): List<Int> =
|
fun getVariablesIndexes(): List<Int> =
|
||||||
ArrayList(associatedVariables)
|
ArrayList(associatedVariables)
|
||||||
|
|
||||||
fun addMergedWith(descriptor: BoxedValueDescriptor) {
|
fun addMergedWith(descriptor: BoxedValueDescriptor) {
|
||||||
mergedWith.add(descriptor)
|
mergedWith.add(descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMergedWith(): Iterable<BoxedValueDescriptor> =
|
fun getMergedWith(): Iterable<BoxedValueDescriptor> =
|
||||||
mergedWith
|
mergedWith
|
||||||
|
|
||||||
fun markAsUnsafeToRemove() {
|
fun markAsUnsafeToRemove() {
|
||||||
isSafeToRemove = false
|
isSafeToRemove = false
|
||||||
@@ -98,7 +98,7 @@ class BoxedValueDescriptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getUnboxingWithCastInsns(): Set<Pair<AbstractInsnNode, Type>> =
|
fun getUnboxingWithCastInsns(): Set<Pair<AbstractInsnNode, Type>> =
|
||||||
unboxingWithCastInsns
|
unboxingWithCastInsns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+83
-79
@@ -36,12 +36,16 @@ import java.util.*
|
|||||||
open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasicInterpreter() {
|
open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasicInterpreter() {
|
||||||
private val boxingPlaces = HashMap<Int, BoxedBasicValue>()
|
private val boxingPlaces = HashMap<Int, BoxedBasicValue>()
|
||||||
|
|
||||||
protected open fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?): BasicValue =
|
protected open fun createNewBoxing(
|
||||||
boxingPlaces.getOrPut(insnList.indexOf(insn)) {
|
insn: AbstractInsnNode,
|
||||||
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator)
|
type: Type,
|
||||||
onNewBoxedValue(boxedBasicValue)
|
progressionIterator: ProgressionIteratorBasicValue?
|
||||||
boxedBasicValue
|
): BasicValue =
|
||||||
}
|
boxingPlaces.getOrPut(insnList.indexOf(insn)) {
|
||||||
|
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator)
|
||||||
|
onNewBoxedValue(boxedBasicValue)
|
||||||
|
boxedBasicValue
|
||||||
|
}
|
||||||
|
|
||||||
protected fun checkUsedValue(value: BasicValue) {
|
protected fun checkUsedValue(value: BasicValue) {
|
||||||
if (value is TaintedBoxedValue) {
|
if (value is TaintedBoxedValue) {
|
||||||
@@ -69,7 +73,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
|||||||
ProgressionIteratorBasicValue.byProgressionClassType(firstArg.type)
|
ProgressionIteratorBasicValue.byProgressionClassType(firstArg.type)
|
||||||
insn.isNextMethodCallOfProgressionIterator(values) -> {
|
insn.isNextMethodCallOfProgressionIterator(values) -> {
|
||||||
val progressionIterator = firstArg as? ProgressionIteratorBasicValue
|
val progressionIterator = firstArg as? ProgressionIteratorBasicValue
|
||||||
?: throw AssertionError("firstArg should be progression iterator")
|
?: throw AssertionError("firstArg should be progression iterator")
|
||||||
createNewBoxing(insn, AsmUtil.boxType(progressionIterator.valuesPrimitiveType), progressionIterator)
|
createNewBoxing(insn, AsmUtil.boxType(progressionIterator.valuesPrimitiveType), progressionIterator)
|
||||||
}
|
}
|
||||||
insn.isAreEqualIntrinsicForSameTypedBoxedValues(values) && canValuesBeUnboxedForAreEqual(values) -> {
|
insn.isAreEqualIntrinsicForSameTypedBoxedValues(values) && canValuesBeUnboxedForAreEqual(values) -> {
|
||||||
@@ -104,30 +108,30 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected open fun isExactValue(value: BasicValue) =
|
protected open fun isExactValue(value: BasicValue) =
|
||||||
value is ProgressionIteratorBasicValue ||
|
value is ProgressionIteratorBasicValue ||
|
||||||
value is CleanBoxedValue ||
|
value is CleanBoxedValue ||
|
||||||
value.type != null && isProgressionClass(value.type)
|
value.type != null && isProgressionClass(value.type)
|
||||||
|
|
||||||
override fun merge(v: BasicValue, w: BasicValue) =
|
override fun merge(v: BasicValue, w: BasicValue) =
|
||||||
when {
|
when {
|
||||||
v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE ->
|
v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE ->
|
||||||
StrictBasicValue.UNINITIALIZED_VALUE
|
StrictBasicValue.UNINITIALIZED_VALUE
|
||||||
v is BoxedBasicValue && w is BoxedBasicValue -> {
|
v is BoxedBasicValue && w is BoxedBasicValue -> {
|
||||||
onMergeSuccess(v, w)
|
onMergeSuccess(v, w)
|
||||||
when {
|
when {
|
||||||
v is TaintedBoxedValue -> v
|
v is TaintedBoxedValue -> v
|
||||||
w is TaintedBoxedValue -> w
|
w is TaintedBoxedValue -> w
|
||||||
v.type != w.type -> v.taint()
|
v.type != w.type -> v.taint()
|
||||||
else -> v
|
else -> v
|
||||||
}
|
|
||||||
}
|
}
|
||||||
v is BoxedBasicValue ->
|
|
||||||
v.taint()
|
|
||||||
w is BoxedBasicValue ->
|
|
||||||
w.taint()
|
|
||||||
else ->
|
|
||||||
super.merge(v, w)
|
|
||||||
}
|
}
|
||||||
|
v is BoxedBasicValue ->
|
||||||
|
v.taint()
|
||||||
|
w is BoxedBasicValue ->
|
||||||
|
w.taint()
|
||||||
|
else ->
|
||||||
|
super.merge(v, w)
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun onNewBoxedValue(value: BoxedBasicValue) {}
|
protected open fun onNewBoxedValue(value: BoxedBasicValue) {}
|
||||||
protected open fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {}
|
protected open fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {}
|
||||||
@@ -140,51 +144,51 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val UNBOXING_METHOD_NAMES =
|
private val UNBOXING_METHOD_NAMES =
|
||||||
ImmutableSet.of("booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue")
|
ImmutableSet.of("booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue")
|
||||||
|
|
||||||
private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYPE, AsmTypes.K_CLASS_TYPE)
|
private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYPE, AsmTypes.K_CLASS_TYPE)
|
||||||
private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE)
|
private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE)
|
||||||
|
|
||||||
fun AbstractInsnNode.isUnboxing() =
|
fun AbstractInsnNode.isUnboxing() =
|
||||||
isPrimitiveUnboxing() || isJavaLangClassUnboxing()
|
isPrimitiveUnboxing() || isJavaLangClassUnboxing()
|
||||||
|
|
||||||
fun AbstractInsnNode.isBoxing() =
|
fun AbstractInsnNode.isBoxing() =
|
||||||
isPrimitiveBoxing() || isJavaLangClassBoxing()
|
isPrimitiveBoxing() || isJavaLangClassBoxing()
|
||||||
|
|
||||||
fun AbstractInsnNode.isPrimitiveUnboxing() =
|
fun AbstractInsnNode.isPrimitiveUnboxing() =
|
||||||
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
|
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
|
||||||
isWrapperClassNameOrNumber(owner) && isUnboxingMethodName(name)
|
isWrapperClassNameOrNumber(owner) && isUnboxingMethodName(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.isJavaLangClassUnboxing() =
|
fun AbstractInsnNode.isJavaLangClassUnboxing() =
|
||||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||||
owner == "kotlin/jvm/JvmClassMappingKt" &&
|
owner == "kotlin/jvm/JvmClassMappingKt" &&
|
||||||
name == "getJavaClass" &&
|
name == "getJavaClass" &&
|
||||||
desc == KCLASS_TO_JLCLASS
|
desc == KCLASS_TO_JLCLASS
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun AbstractInsnNode.isMethodInsnWith(opcode: Int, condition: MethodInsnNode.() -> Boolean): Boolean =
|
inline fun AbstractInsnNode.isMethodInsnWith(opcode: Int, condition: MethodInsnNode.() -> Boolean): Boolean =
|
||||||
this.opcode == opcode && this is MethodInsnNode && this.condition()
|
this.opcode == opcode && this is MethodInsnNode && this.condition()
|
||||||
|
|
||||||
private fun isWrapperClassNameOrNumber(internalClassName: String) =
|
private fun isWrapperClassNameOrNumber(internalClassName: String) =
|
||||||
isWrapperClassName(internalClassName) || internalClassName == Type.getInternalName(Number::class.java)
|
isWrapperClassName(internalClassName) || internalClassName == Type.getInternalName(Number::class.java)
|
||||||
|
|
||||||
private fun isWrapperClassName(internalClassName: String) =
|
private fun isWrapperClassName(internalClassName: String) =
|
||||||
JvmPrimitiveType.isWrapperClassName(buildFqNameByInternal(internalClassName))
|
JvmPrimitiveType.isWrapperClassName(buildFqNameByInternal(internalClassName))
|
||||||
|
|
||||||
|
|
||||||
private fun buildFqNameByInternal(internalClassName: String) =
|
private fun buildFqNameByInternal(internalClassName: String) =
|
||||||
FqName(Type.getObjectType(internalClassName).className)
|
FqName(Type.getObjectType(internalClassName).className)
|
||||||
|
|
||||||
private fun isUnboxingMethodName(name: String) =
|
private fun isUnboxingMethodName(name: String) =
|
||||||
UNBOXING_METHOD_NAMES.contains(name)
|
UNBOXING_METHOD_NAMES.contains(name)
|
||||||
|
|
||||||
fun AbstractInsnNode.isPrimitiveBoxing() =
|
fun AbstractInsnNode.isPrimitiveBoxing() =
|
||||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||||
isWrapperClassName(owner) &&
|
isWrapperClassName(owner) &&
|
||||||
name == "valueOf" &&
|
name == "valueOf" &&
|
||||||
isBoxingMethodDescriptor()
|
isBoxingMethodDescriptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean {
|
private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean {
|
||||||
val ownerType = Type.getObjectType(owner)
|
val ownerType = Type.getObjectType(owner)
|
||||||
@@ -192,58 +196,58 @@ private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.isJavaLangClassBoxing() =
|
fun AbstractInsnNode.isJavaLangClassBoxing() =
|
||||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||||
owner == AsmTypes.REFLECTION &&
|
owner == AsmTypes.REFLECTION &&
|
||||||
name == "getOrCreateKotlinClass" &&
|
name == "getOrCreateKotlinClass" &&
|
||||||
desc == JLCLASS_TO_KCLASS
|
desc == JLCLASS_TO_KCLASS
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
|
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
|
||||||
values.firstOrNull() is ProgressionIteratorBasicValue &&
|
values.firstOrNull() is ProgressionIteratorBasicValue &&
|
||||||
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
||||||
name == "next"
|
name == "next"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>) =
|
fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>) =
|
||||||
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
||||||
val firstArgType = values.firstOrNull()?.type
|
val firstArgType = values.firstOrNull()?.type
|
||||||
firstArgType != null &&
|
firstArgType != null &&
|
||||||
isProgressionClass(firstArgType) &&
|
isProgressionClass(firstArgType) &&
|
||||||
name == "iterator"
|
name == "iterator"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isProgressionClass(type: Type) =
|
fun isProgressionClass(type: Type) =
|
||||||
isRangeOrProgression(buildFqNameByInternal(type.internalName))
|
isRangeOrProgression(buildFqNameByInternal(type.internalName))
|
||||||
|
|
||||||
fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) =
|
fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) =
|
||||||
isAreEqualIntrinsic() && areSameTypedBoxedValues(values)
|
isAreEqualIntrinsic() && areSameTypedBoxedValues(values)
|
||||||
|
|
||||||
fun areSameTypedBoxedValues(values: List<BasicValue>): Boolean {
|
fun areSameTypedBoxedValues(values: List<BasicValue>): Boolean {
|
||||||
if (values.size != 2) return false
|
if (values.size != 2) return false
|
||||||
val (v1, v2) = values
|
val (v1, v2) = values
|
||||||
return v1 is BoxedBasicValue &&
|
return v1 is BoxedBasicValue &&
|
||||||
v2 is BoxedBasicValue &&
|
v2 is BoxedBasicValue &&
|
||||||
v1.descriptor.unboxedType == v2.descriptor.unboxedType
|
v1.descriptor.unboxedType == v2.descriptor.unboxedType
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.isAreEqualIntrinsic() =
|
fun AbstractInsnNode.isAreEqualIntrinsic() =
|
||||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||||
name == "areEqual" &&
|
name == "areEqual" &&
|
||||||
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
||||||
desc == "(Ljava/lang/Object;Ljava/lang/Object;)Z"
|
desc == "(Ljava/lang/Object;Ljava/lang/Object;)Z"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val shouldUseEqualsForWrappers = setOf(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, AsmTypes.JAVA_CLASS_TYPE)
|
private val shouldUseEqualsForWrappers = setOf(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, AsmTypes.JAVA_CLASS_TYPE)
|
||||||
|
|
||||||
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>): Boolean =
|
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>): Boolean =
|
||||||
values.none { getUnboxedType(it.type) in shouldUseEqualsForWrappers }
|
values.none { getUnboxedType(it.type) in shouldUseEqualsForWrappers }
|
||||||
|
|
||||||
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
|
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
|
||||||
isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values)
|
isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values)
|
||||||
|
|
||||||
fun AbstractInsnNode.isJavaLangComparableCompareTo() =
|
fun AbstractInsnNode.isJavaLangComparableCompareTo() =
|
||||||
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
||||||
name == "compareTo" &&
|
name == "compareTo" &&
|
||||||
owner == "java/lang/Comparable" &&
|
owner == "java/lang/Comparable" &&
|
||||||
desc == "(Ljava/lang/Object;)I"
|
desc == "(Ljava/lang/Object;)I"
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-33
@@ -44,11 +44,11 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inline fun Transformation(crossinline body: (AbstractInsnNode) -> Unit): Transformation =
|
private inline fun Transformation(crossinline body: (AbstractInsnNode) -> Unit): Transformation =
|
||||||
object : Transformation {
|
object : Transformation {
|
||||||
override fun apply(insn: AbstractInsnNode) {
|
override fun apply(insn: AbstractInsnNode) {
|
||||||
body(insn)
|
body(insn)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val REPLACE_WITH_NOP = Transformation { insnList.set(it, createRemovableNopInsn()) }
|
private val REPLACE_WITH_NOP = Transformation { insnList.set(it, createRemovableNopInsn()) }
|
||||||
private val REPLACE_WITH_POP1 = Transformation { insnList.set(it, InsnNode(Opcodes.POP)) }
|
private val REPLACE_WITH_POP1 = Transformation { insnList.set(it, InsnNode(Opcodes.POP)) }
|
||||||
@@ -145,7 +145,12 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
|||||||
return super.binaryOperation(insn, value1, value2)
|
return super.binaryOperation(insn, value1, value2)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ternaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue, value3: SourceValue): SourceValue {
|
override fun ternaryOperation(
|
||||||
|
insn: AbstractInsnNode,
|
||||||
|
value1: SourceValue,
|
||||||
|
value2: SourceValue,
|
||||||
|
value3: SourceValue
|
||||||
|
): SourceValue {
|
||||||
value1.insns.markAsDontTouch()
|
value1.insns.markAsDontTouch()
|
||||||
value2.insns.markAsDontTouch()
|
value2.insns.markAsDontTouch()
|
||||||
value3.insns.markAsDontTouch()
|
value3.insns.markAsDontTouch()
|
||||||
@@ -193,8 +198,7 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
|||||||
if (sources.all { !isDontTouch(it) } && sources.any { isTransformableCheckcastOperand(it, resultType) }) {
|
if (sources.all { !isDontTouch(it) } && sources.any { isTransformableCheckcastOperand(it, resultType) }) {
|
||||||
transformations[insn] = replaceWithNopTransformation()
|
transformations[insn] = replaceWithNopTransformation()
|
||||||
sources.forEach { propagatePopBackwards(it, inputTop.size) }
|
sources.forEach { propagatePopBackwards(it, inputTop.size) }
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
transformations[insn] = insertPopAfterTransformation(poppedValueSize)
|
transformations[insn] = insertPopAfterTransformation(poppedValueSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,8 +218,7 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
|||||||
if (sources.all { !isDontTouch(it) }) {
|
if (sources.all { !isDontTouch(it) }) {
|
||||||
transformations[insn] = replaceWithNopTransformation()
|
transformations[insn] = replaceWithNopTransformation()
|
||||||
sources.forEach { propagatePopBackwards(it, inputTop.size) }
|
sources.forEach { propagatePopBackwards(it, inputTop.size) }
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
transformations[insn] = replaceWithPopTransformation(poppedValueSize)
|
transformations[insn] = replaceWithPopTransformation(poppedValueSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,8 +255,7 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
|||||||
while (node != null && node != end) {
|
while (node != null && node != end) {
|
||||||
if (node in removableNops && !keepNop) {
|
if (node in removableNops && !keepNop) {
|
||||||
node = insnList.removeNodeGetNext(node)
|
node = insnList.removeNodeGetNext(node)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (node.isMeaningful) keepNop = false
|
if (node.isMeaningful) keepNop = false
|
||||||
node = node.next
|
node = node.next
|
||||||
}
|
}
|
||||||
@@ -261,24 +263,24 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun replaceWithPopTransformation(size: Int): Transformation =
|
private fun replaceWithPopTransformation(size: Int): Transformation =
|
||||||
when (size) {
|
when (size) {
|
||||||
1 -> REPLACE_WITH_POP1
|
1 -> REPLACE_WITH_POP1
|
||||||
2 -> REPLACE_WITH_POP2
|
2 -> REPLACE_WITH_POP2
|
||||||
else -> throw AssertionError("Unexpected pop value size: $size")
|
else -> throw AssertionError("Unexpected pop value size: $size")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun insertPopAfterTransformation(size: Int): Transformation =
|
private fun insertPopAfterTransformation(size: Int): Transformation =
|
||||||
when (size) {
|
when (size) {
|
||||||
1 -> INSERT_POP1_AFTER
|
1 -> INSERT_POP1_AFTER
|
||||||
2 -> INSERT_POP2_AFTER
|
2 -> INSERT_POP2_AFTER
|
||||||
else -> throw AssertionError("Unexpected pop value size: $size")
|
else -> throw AssertionError("Unexpected pop value size: $size")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun replaceWithNopTransformation(): Transformation =
|
private fun replaceWithNopTransformation(): Transformation =
|
||||||
REPLACE_WITH_NOP
|
REPLACE_WITH_NOP
|
||||||
|
|
||||||
private fun createRemovableNopInsn() =
|
private fun createRemovableNopInsn() =
|
||||||
InsnNode(Opcodes.NOP).apply { removableNops.add(this) }
|
InsnNode(Opcodes.NOP).apply { removableNops.add(this) }
|
||||||
|
|
||||||
private fun getInputTop(insn: AbstractInsnNode): SourceValue {
|
private fun getInputTop(insn: AbstractInsnNode): SourceValue {
|
||||||
val i = insnList.indexOf(insn)
|
val i = insnList.indexOf(insn)
|
||||||
@@ -287,28 +289,28 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isTransformableCheckcastOperand(it: AbstractInsnNode, resultType: String) =
|
private fun isTransformableCheckcastOperand(it: AbstractInsnNode, resultType: String) =
|
||||||
it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType
|
it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType
|
||||||
|
|
||||||
private fun isTransformablePopOperand(insn: AbstractInsnNode) =
|
private fun isTransformablePopOperand(insn: AbstractInsnNode) =
|
||||||
insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isPurePush()
|
insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isPurePush()
|
||||||
|
|
||||||
private fun isDontTouch(insn: AbstractInsnNode) =
|
private fun isDontTouch(insn: AbstractInsnNode) =
|
||||||
dontTouchInsnIndices[insnList.indexOf(insn)]
|
dontTouchInsnIndices[insnList.indexOf(insn)]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.isPurePush() =
|
fun AbstractInsnNode.isPurePush() =
|
||||||
isLoadOperation() ||
|
isLoadOperation() ||
|
||||||
opcode in Opcodes.ACONST_NULL .. Opcodes.LDC + 2 ||
|
opcode in Opcodes.ACONST_NULL..Opcodes.LDC + 2 ||
|
||||||
isUnitInstance()
|
isUnitInstance()
|
||||||
|
|
||||||
fun AbstractInsnNode.isPop() =
|
fun AbstractInsnNode.isPop() =
|
||||||
opcode == Opcodes.POP || opcode == Opcodes.POP2
|
opcode == Opcodes.POP || opcode == Opcodes.POP2
|
||||||
|
|
||||||
fun AbstractInsnNode.isUnitInstance() =
|
fun AbstractInsnNode.isUnitInstance() =
|
||||||
opcode == Opcodes.GETSTATIC &&
|
opcode == Opcodes.GETSTATIC &&
|
||||||
this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
|
this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
|
||||||
|
|
||||||
fun AbstractInsnNode.isPrimitiveTypeConversion() =
|
fun AbstractInsnNode.isPrimitiveTypeConversion() =
|
||||||
opcode in Opcodes.I2L .. Opcodes.I2S
|
opcode in Opcodes.I2L..Opcodes.I2S
|
||||||
+2
@@ -30,6 +30,7 @@ import org.jetbrains.org.objectweb.asm.Type;
|
|||||||
|
|
||||||
public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
||||||
private final static ImmutableMap<String, Type> VALUES_TYPENAME_TO_TYPE;
|
private final static ImmutableMap<String, Type> VALUES_TYPENAME_TO_TYPE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ImmutableMap.Builder<String, Type> builder = ImmutableMap.builder();
|
ImmutableMap.Builder<String, Type> builder = ImmutableMap.builder();
|
||||||
for (PrimitiveType primitiveType : RangeCodegenUtilKt.getSupportedRangeTypes()) {
|
for (PrimitiveType primitiveType : RangeCodegenUtilKt.getSupportedRangeTypes()) {
|
||||||
@@ -39,6 +40,7 @@ public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final ImmutableMap<PrimitiveType, ProgressionIteratorBasicValue> ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE;
|
private static final ImmutableMap<PrimitiveType, ProgressionIteratorBasicValue> ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ImmutableMap.Builder<PrimitiveType, ProgressionIteratorBasicValue> builder = ImmutableMap.builder();
|
ImmutableMap.Builder<PrimitiveType, ProgressionIteratorBasicValue> builder = ImmutableMap.builder();
|
||||||
for (PrimitiveType elementType : RangeCodegenUtilKt.getSupportedRangeTypes()) {
|
for (PrimitiveType elementType : RangeCodegenUtilKt.getSupportedRangeTypes()) {
|
||||||
|
|||||||
+13
-14
@@ -116,8 +116,7 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
|||||||
|
|
||||||
if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.opcode)) {
|
if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.opcode)) {
|
||||||
markValueAsDirty(value)
|
markValueAsDirty(value)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
addAssociatedInsn(value, insnNode)
|
addAssociatedInsn(value, insnNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,22 +128,22 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val PERMITTED_OPERATIONS_OPCODES =
|
private val PERMITTED_OPERATIONS_OPCODES =
|
||||||
ImmutableSet.of(Opcodes.ASTORE, Opcodes.ALOAD, Opcodes.POP, Opcodes.DUP, Opcodes.CHECKCAST, Opcodes.INSTANCEOF)
|
ImmutableSet.of(Opcodes.ASTORE, Opcodes.ALOAD, Opcodes.POP, Opcodes.DUP, Opcodes.CHECKCAST, Opcodes.INSTANCEOF)
|
||||||
|
|
||||||
private val PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER =
|
private val PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER =
|
||||||
ImmutableSet.of(Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE)
|
ImmutableSet.of(Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE)
|
||||||
|
|
||||||
private fun isSafeCast(value: BoxedBasicValue, targetInternalName: String) =
|
private fun isSafeCast(value: BoxedBasicValue, targetInternalName: String) =
|
||||||
when (targetInternalName) {
|
when (targetInternalName) {
|
||||||
Type.getInternalName(Any::class.java) ->
|
Type.getInternalName(Any::class.java) ->
|
||||||
true
|
true
|
||||||
Type.getInternalName(Number::class.java) ->
|
Type.getInternalName(Number::class.java) ->
|
||||||
PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(value.descriptor.unboxedType.sort)
|
PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(value.descriptor.unboxedType.sort)
|
||||||
"java/lang/Comparable" ->
|
"java/lang/Comparable" ->
|
||||||
true
|
true
|
||||||
else ->
|
else ->
|
||||||
value.type.internalName == targetInternalName
|
value.type.internalName == targetInternalName
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addAssociatedInsn(value: BoxedBasicValue, insn: AbstractInsnNode) {
|
private fun addAssociatedInsn(value: BoxedBasicValue, insn: AbstractInsnNode) {
|
||||||
value.descriptor.run {
|
value.descriptor.run {
|
||||||
|
|||||||
+55
-61
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.codegen.inline.insnOpcodeText
|
|||||||
import org.jetbrains.kotlin.codegen.inline.insnText
|
import org.jetbrains.kotlin.codegen.inline.insnText
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
import org.jetbrains.kotlin.codegen.optimization.common.remapLocalVariables
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
@@ -32,7 +32,6 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class RedundantBoxingMethodTransformer : MethodTransformer() {
|
class RedundantBoxingMethodTransformer : MethodTransformer() {
|
||||||
@@ -58,9 +57,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun interpretPopInstructionsForBoxedValues(
|
private fun interpretPopInstructionsForBoxedValues(
|
||||||
interpreter: RedundantBoxingInterpreter,
|
interpreter: RedundantBoxingInterpreter,
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
frames: Array<out Frame<BasicValue>?>
|
frames: Array<out Frame<BasicValue>?>
|
||||||
) {
|
) {
|
||||||
for (i in frames.indices) {
|
for (i in frames.indices) {
|
||||||
val insn = node.instructions[i]
|
val insn = node.instructions[i]
|
||||||
@@ -80,9 +79,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeValuesClashingWithVariables(
|
private fun removeValuesClashingWithVariables(
|
||||||
values: RedundantBoxedValuesCollection,
|
values: RedundantBoxedValuesCollection,
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
frames: Array<Frame<BasicValue>>
|
frames: Array<Frame<BasicValue>>
|
||||||
) {
|
) {
|
||||||
while (removeValuesClashingWithVariablesPass(values, node, frames)) {
|
while (removeValuesClashingWithVariablesPass(values, node, frames)) {
|
||||||
// do nothing
|
// do nothing
|
||||||
@@ -90,9 +89,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeValuesClashingWithVariablesPass(
|
private fun removeValuesClashingWithVariablesPass(
|
||||||
values: RedundantBoxedValuesCollection,
|
values: RedundantBoxedValuesCollection,
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
frames: Array<out Frame<BasicValue>?>
|
frames: Array<out Frame<BasicValue>?>
|
||||||
): Boolean {
|
): Boolean {
|
||||||
var needToRepeat = false
|
var needToRepeat = false
|
||||||
|
|
||||||
@@ -123,13 +122,13 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isUnsafeToRemoveBoxingForConnectedValues(usedValues: List<BasicValue>, unboxedType: Type): Boolean =
|
private fun isUnsafeToRemoveBoxingForConnectedValues(usedValues: List<BasicValue>, unboxedType: Type): Boolean =
|
||||||
usedValues.any { input ->
|
usedValues.any { input ->
|
||||||
if (input === StrictBasicValue.UNINITIALIZED_VALUE) return@any false
|
if (input === StrictBasicValue.UNINITIALIZED_VALUE) return@any false
|
||||||
if (input !is BoxedBasicValue) return@any true
|
if (input !is BoxedBasicValue) return@any true
|
||||||
|
|
||||||
val descriptor = input.descriptor
|
val descriptor = input.descriptor
|
||||||
!descriptor.isSafeToRemove || descriptor.unboxedType != unboxedType
|
!descriptor.isSafeToRemove || descriptor.unboxedType != unboxedType
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun adaptLocalVariableTableForBoxedValues(node: MethodNode, frames: Array<Frame<BasicValue>>) {
|
private fun adaptLocalVariableTableForBoxedValues(node: MethodNode, frames: Array<Frame<BasicValue>>) {
|
||||||
for (localVariableNode in node.localVariables) {
|
for (localVariableNode in node.localVariables) {
|
||||||
@@ -148,9 +147,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getValuesStoredOrLoadedToVariable(
|
private fun getValuesStoredOrLoadedToVariable(
|
||||||
localVariableNode: LocalVariableNode,
|
localVariableNode: LocalVariableNode,
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
frames: Array<out Frame<BasicValue>?>
|
frames: Array<out Frame<BasicValue>?>
|
||||||
): List<BasicValue> {
|
): List<BasicValue> {
|
||||||
val values = ArrayList<BasicValue>()
|
val values = ArrayList<BasicValue>()
|
||||||
val insnList = node.instructions
|
val insnList = node.instructions
|
||||||
@@ -171,8 +170,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
(insn as VarInsnNode).`var` == localVariableNode.index) {
|
(insn as VarInsnNode).`var` == localVariableNode.index) {
|
||||||
if (insn.getOpcode() == Opcodes.ASTORE) {
|
if (insn.getOpcode() == Opcodes.ASTORE) {
|
||||||
values.add(frame.top()!!)
|
values.add(frame.top()!!)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
values.add(frame.getLocal(insn.`var`))
|
values.add(frame.getLocal(insn.`var`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,8 +203,8 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun adaptInstructionsForBoxedValues(
|
private fun adaptInstructionsForBoxedValues(
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
values: RedundantBoxedValuesCollection
|
values: RedundantBoxedValuesCollection
|
||||||
) {
|
) {
|
||||||
for (value in values) {
|
for (value in values) {
|
||||||
adaptInstructionsForBoxedValue(node, value)
|
adaptInstructionsForBoxedValue(node, value)
|
||||||
@@ -228,8 +226,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
private fun adaptBoxingInstruction(node: MethodNode, value: BoxedValueDescriptor) {
|
private fun adaptBoxingInstruction(node: MethodNode, value: BoxedValueDescriptor) {
|
||||||
if (!value.isFromProgressionIterator()) {
|
if (!value.isFromProgressionIterator()) {
|
||||||
node.instructions.remove(value.boxingInsn)
|
node.instructions.remove(value.boxingInsn)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val iterator = value.progressionIterator ?: error("iterator should not be null because isFromProgressionIterator returns true")
|
val iterator = value.progressionIterator ?: error("iterator should not be null because isFromProgressionIterator returns true")
|
||||||
|
|
||||||
//add checkcast to kotlin/<T>Iterator before next() call
|
//add checkcast to kotlin/<T>Iterator before next() call
|
||||||
@@ -237,20 +234,20 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
|
|
||||||
//invoke concrete method (kotlin/<T>iterator.next<T>())
|
//invoke concrete method (kotlin/<T>iterator.next<T>())
|
||||||
node.instructions.set(
|
node.instructions.set(
|
||||||
value.boxingInsn,
|
value.boxingInsn,
|
||||||
MethodInsnNode(
|
MethodInsnNode(
|
||||||
Opcodes.INVOKEVIRTUAL,
|
Opcodes.INVOKEVIRTUAL,
|
||||||
iterator.type.internalName, iterator.nextMethodName, iterator.nextMethodDesc,
|
iterator.type.internalName, iterator.nextMethodName, iterator.nextMethodDesc,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun adaptCastInstruction(
|
private fun adaptCastInstruction(
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
value: BoxedValueDescriptor,
|
value: BoxedValueDescriptor,
|
||||||
castWithType: Pair<AbstractInsnNode, Type>
|
castWithType: Pair<AbstractInsnNode, Type>
|
||||||
) {
|
) {
|
||||||
val castInsn = castWithType.getFirst()
|
val castInsn = castWithType.getFirst()
|
||||||
val castInsnsListener = MethodNode(Opcodes.ASM5)
|
val castInsnsListener = MethodNode(Opcodes.ASM5)
|
||||||
@@ -264,7 +261,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun adaptInstruction(
|
private fun adaptInstruction(
|
||||||
node: MethodNode, insn: AbstractInsnNode, value: BoxedValueDescriptor
|
node: MethodNode, insn: AbstractInsnNode, value: BoxedValueDescriptor
|
||||||
) {
|
) {
|
||||||
val isDoubleSize = value.isDoubleSize()
|
val isDoubleSize = value.isDoubleSize()
|
||||||
|
|
||||||
@@ -286,8 +283,8 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
|
|
||||||
Opcodes.INSTANCEOF -> {
|
Opcodes.INSTANCEOF -> {
|
||||||
node.instructions.insertBefore(
|
node.instructions.insertBefore(
|
||||||
insn,
|
insn,
|
||||||
InsnNode(if (isDoubleSize) Opcodes.POP2 else Opcodes.POP)
|
InsnNode(if (isDoubleSize) Opcodes.POP2 else Opcodes.POP)
|
||||||
)
|
)
|
||||||
node.instructions.set(insn, InsnNode(Opcodes.ICONST_1))
|
node.instructions.set(insn, InsnNode(Opcodes.ICONST_1))
|
||||||
}
|
}
|
||||||
@@ -299,7 +296,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
insn.isJavaLangComparableCompareTo() ->
|
insn.isJavaLangComparableCompareTo() ->
|
||||||
adaptJavaLangComparableCompareTo(node, insn, value)
|
adaptJavaLangComparableCompareTo(node, insn, value)
|
||||||
insn.isJavaLangClassBoxing() ||
|
insn.isJavaLangClassBoxing() ||
|
||||||
insn.isJavaLangClassUnboxing() ->
|
insn.isJavaLangClassUnboxing() ->
|
||||||
node.instructions.remove(insn)
|
node.instructions.remove(insn)
|
||||||
else ->
|
else ->
|
||||||
throwCannotAdaptInstruction(insn)
|
throwCannotAdaptInstruction(insn)
|
||||||
@@ -309,8 +306,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
Opcodes.INVOKEINTERFACE -> {
|
Opcodes.INVOKEINTERFACE -> {
|
||||||
if (insn.isJavaLangComparableCompareTo()) {
|
if (insn.isJavaLangComparableCompareTo()) {
|
||||||
adaptJavaLangComparableCompareTo(node, insn, value)
|
adaptJavaLangComparableCompareTo(node, insn, value)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
throwCannotAdaptInstruction(insn)
|
throwCannotAdaptInstruction(insn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,12 +321,12 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun throwCannotAdaptInstruction(insn: AbstractInsnNode): Nothing =
|
private fun throwCannotAdaptInstruction(insn: AbstractInsnNode): Nothing =
|
||||||
throw AssertionError("Cannot adapt instruction: ${insn.insnText}")
|
throw AssertionError("Cannot adapt instruction: ${insn.insnText}")
|
||||||
|
|
||||||
private fun adaptAreEqualIntrinsic(
|
private fun adaptAreEqualIntrinsic(
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
insn: AbstractInsnNode,
|
insn: AbstractInsnNode,
|
||||||
value: BoxedValueDescriptor
|
value: BoxedValueDescriptor
|
||||||
) {
|
) {
|
||||||
val unboxedType = value.unboxedType
|
val unboxedType = value.unboxedType
|
||||||
|
|
||||||
@@ -339,8 +335,8 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
adaptAreEqualIntrinsicForInt(node, insn)
|
adaptAreEqualIntrinsicForInt(node, insn)
|
||||||
Type.LONG ->
|
Type.LONG ->
|
||||||
adaptAreEqualIntrinsicForLong(node, insn)
|
adaptAreEqualIntrinsicForLong(node, insn)
|
||||||
Type.OBJECT ->
|
Type.OBJECT -> {
|
||||||
{}
|
}
|
||||||
else ->
|
else ->
|
||||||
throw AssertionError("Unexpected unboxed type kind: $unboxedType")
|
throw AssertionError("Unexpected unboxed type kind: $unboxedType")
|
||||||
}
|
}
|
||||||
@@ -353,8 +349,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
fuseAreEqualWithBranch(node, insn, Opcodes.IF_ICMPNE, Opcodes.IF_ICMPEQ)
|
fuseAreEqualWithBranch(node, insn, Opcodes.IF_ICMPNE, Opcodes.IF_ICMPEQ)
|
||||||
remove(insn)
|
remove(insn)
|
||||||
remove(next)
|
remove(next)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ifEqual1Else0(node, insn, Opcodes.IF_ICMPNE)
|
ifEqual1Else0(node, insn, Opcodes.IF_ICMPNE)
|
||||||
remove(insn)
|
remove(insn)
|
||||||
}
|
}
|
||||||
@@ -369,8 +364,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
fuseAreEqualWithBranch(node, insn, Opcodes.IFNE, Opcodes.IFEQ)
|
fuseAreEqualWithBranch(node, insn, Opcodes.IFNE, Opcodes.IFEQ)
|
||||||
remove(insn)
|
remove(insn)
|
||||||
remove(next)
|
remove(next)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ifEqual1Else0(node, insn, Opcodes.IFNE)
|
ifEqual1Else0(node, insn, Opcodes.IFNE)
|
||||||
remove(insn)
|
remove(insn)
|
||||||
}
|
}
|
||||||
@@ -378,10 +372,10 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun fuseAreEqualWithBranch(
|
private fun fuseAreEqualWithBranch(
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
insn: AbstractInsnNode,
|
insn: AbstractInsnNode,
|
||||||
ifEqualOpcode: Int,
|
ifEqualOpcode: Int,
|
||||||
ifNotEqualOpcode: Int
|
ifNotEqualOpcode: Int
|
||||||
) {
|
) {
|
||||||
node.instructions.run {
|
node.instructions.run {
|
||||||
val next = insn.next
|
val next = insn.next
|
||||||
@@ -412,9 +406,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun adaptJavaLangComparableCompareTo(
|
private fun adaptJavaLangComparableCompareTo(
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
insn: AbstractInsnNode,
|
insn: AbstractInsnNode,
|
||||||
value: BoxedValueDescriptor
|
value: BoxedValueDescriptor
|
||||||
) {
|
) {
|
||||||
val unboxedType = value.unboxedType
|
val unboxedType = value.unboxedType
|
||||||
|
|
||||||
@@ -438,15 +432,15 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
|
|||||||
val next2 = next?.next
|
val next2 = next?.next
|
||||||
when {
|
when {
|
||||||
next != null && next2 != null &&
|
next != null && next2 != null &&
|
||||||
next.opcode == Opcodes.ICONST_0 &&
|
next.opcode == Opcodes.ICONST_0 &&
|
||||||
next2.opcode >= Opcodes.IF_ICMPEQ && next2.opcode <= Opcodes.IF_ICMPLE -> {
|
next2.opcode >= Opcodes.IF_ICMPEQ && next2.opcode <= Opcodes.IF_ICMPLE -> {
|
||||||
// Fuse: compareTo + ICONST_0 + IF_ICMPxx -> IF_ICMPxx
|
// Fuse: compareTo + ICONST_0 + IF_ICMPxx -> IF_ICMPxx
|
||||||
remove(insn)
|
remove(insn)
|
||||||
remove(next)
|
remove(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
next != null &&
|
next != null &&
|
||||||
next.opcode >= Opcodes.IFEQ && next.opcode <= Opcodes.IFLE -> {
|
next.opcode >= Opcodes.IFEQ && next.opcode <= Opcodes.IFLE -> {
|
||||||
// Fuse: compareTo + IFxx -> IF_ICMPxx
|
// Fuse: compareTo + IFxx -> IF_ICMPxx
|
||||||
val nextLabel = (next as JumpInsnNode).label
|
val nextLabel = (next as JumpInsnNode).label
|
||||||
val ifCmpOpcode = next.opcode - Opcodes.IFEQ + Opcodes.IF_ICMPEQ
|
val ifCmpOpcode = next.opcode - Opcodes.IFEQ + Opcodes.IF_ICMPEQ
|
||||||
|
|||||||
+14
-15
@@ -84,8 +84,7 @@ class StackPeepholeOptimizationsTransformer : MethodTransformer() {
|
|||||||
it.set(insn, InsnNode(Opcodes.NOP))
|
it.set(insn, InsnNode(Opcodes.NOP))
|
||||||
it.remove(prevNonNop)
|
it.remove(prevNonNop)
|
||||||
}
|
}
|
||||||
}
|
} else if (i > 1) {
|
||||||
else if (i > 1) {
|
|
||||||
val prevNonNop2 = prevNonNop.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn
|
val prevNonNop2 = prevNonNop.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn
|
||||||
if (prevNonNop.isEliminatedByPop() && prevNonNop2.isEliminatedByPop()) {
|
if (prevNonNop.isEliminatedByPop() && prevNonNop2.isEliminatedByPop()) {
|
||||||
actions.add {
|
actions.add {
|
||||||
@@ -112,24 +111,24 @@ class StackPeepholeOptimizationsTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun AbstractInsnNode.isEliminatedByPop() =
|
private fun AbstractInsnNode.isEliminatedByPop() =
|
||||||
isPurePushOfSize1() ||
|
isPurePushOfSize1() ||
|
||||||
opcode == Opcodes.DUP
|
opcode == Opcodes.DUP
|
||||||
|
|
||||||
private fun AbstractInsnNode.isPurePushOfSize1(): Boolean =
|
private fun AbstractInsnNode.isPurePushOfSize1(): Boolean =
|
||||||
opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 ||
|
opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 ||
|
||||||
opcode in Opcodes.BIPUSH..Opcodes.ILOAD ||
|
opcode in Opcodes.BIPUSH..Opcodes.ILOAD ||
|
||||||
opcode == Opcodes.FLOAD ||
|
opcode == Opcodes.FLOAD ||
|
||||||
opcode == Opcodes.ALOAD ||
|
opcode == Opcodes.ALOAD ||
|
||||||
isUnitInstance()
|
isUnitInstance()
|
||||||
|
|
||||||
private fun AbstractInsnNode.isEliminatedByPop2() =
|
private fun AbstractInsnNode.isEliminatedByPop2() =
|
||||||
isPurePushOfSize2() ||
|
isPurePushOfSize2() ||
|
||||||
opcode == Opcodes.DUP2
|
opcode == Opcodes.DUP2
|
||||||
|
|
||||||
private fun AbstractInsnNode.isPurePushOfSize2(): Boolean =
|
private fun AbstractInsnNode.isPurePushOfSize2(): Boolean =
|
||||||
opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 ||
|
opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 ||
|
||||||
opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 ||
|
opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 ||
|
||||||
opcode == Opcodes.LLOAD ||
|
opcode == Opcodes.LLOAD ||
|
||||||
opcode == Opcodes.DLOAD
|
opcode == Opcodes.DLOAD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -22,8 +22,8 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
||||||
|
|
||||||
class CustomFramesMethodAnalyzer<V : Value>(
|
class CustomFramesMethodAnalyzer<V : Value>(
|
||||||
owner: String, method: MethodNode, interpreter: Interpreter<V>,
|
owner: String, method: MethodNode, interpreter: Interpreter<V>,
|
||||||
private val frameFactory: (Int, Int) -> Frame<V>
|
private val frameFactory: (Int, Int) -> Frame<V>
|
||||||
) : MethodAnalyzer<V>(owner, method, interpreter) {
|
) : MethodAnalyzer<V>(owner, method, interpreter) {
|
||||||
override fun newFrame(nLocals: Int, nStack: Int) = frameFactory(nLocals, nStack)
|
override fun newFrame(nLocals: Int, nStack: Int) = frameFactory(nLocals, nStack)
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-19
@@ -62,9 +62,9 @@ import java.util.*
|
|||||||
* @author Dmitry Petrov
|
* @author Dmitry Petrov
|
||||||
*/
|
*/
|
||||||
open class MethodAnalyzer<V : Value>(
|
open class MethodAnalyzer<V : Value>(
|
||||||
val owner: String,
|
val owner: String,
|
||||||
val method: MethodNode,
|
val method: MethodNode,
|
||||||
protected val interpreter: Interpreter<V>
|
protected val interpreter: Interpreter<V>
|
||||||
) {
|
) {
|
||||||
val instructions: InsnList = method.instructions
|
val instructions: InsnList = method.instructions
|
||||||
private val nInsns: Int = instructions.size()
|
private val nInsns: Int = instructions.size()
|
||||||
@@ -91,7 +91,7 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
protected open fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean = true
|
protected open fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean = true
|
||||||
|
|
||||||
protected open fun visitControlFlowExceptionEdge(insn: Int, tcb: TryCatchBlockNode): Boolean =
|
protected open fun visitControlFlowExceptionEdge(insn: Int, tcb: TryCatchBlockNode): Boolean =
|
||||||
visitControlFlowExceptionEdge(insn, instructions.indexOf(tcb.handler))
|
visitControlFlowExceptionEdge(insn, instructions.indexOf(tcb.handler))
|
||||||
|
|
||||||
fun analyze(): Array<Frame<V>?> {
|
fun analyze(): Array<Frame<V>?> {
|
||||||
if (nInsns == 0) return frames
|
if (nInsns == 0) return frames
|
||||||
@@ -116,8 +116,7 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
|
|
||||||
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
|
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
|
||||||
visitNopInsn(f, insn)
|
visitNopInsn(f, insn)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
current.init(f).execute(insnNode, interpreter)
|
current.init(f).execute(insnNode, interpreter)
|
||||||
|
|
||||||
when {
|
when {
|
||||||
@@ -129,12 +128,13 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
visitTableSwitchInsnNode(insnNode, current, insn)
|
visitTableSwitchInsnNode(insnNode, current, insn)
|
||||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
||||||
visitOpInsn(current, insn)
|
visitOpInsn(current, insn)
|
||||||
else -> {}
|
else -> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers[insn]?.forEach { tcb ->
|
handlers[insn]?.forEach { tcb ->
|
||||||
val exnType = Type.getObjectType(tcb.type?:"java/lang/Throwable")
|
val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable")
|
||||||
val jump = instructions.indexOf(tcb.handler)
|
val jump = instructions.indexOf(tcb.handler)
|
||||||
if (visitControlFlowExceptionEdge(insn, tcb)) {
|
if (visitControlFlowExceptionEdge(insn, tcb)) {
|
||||||
handler.init(f)
|
handler.init(f)
|
||||||
@@ -144,11 +144,9 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} catch (e: AnalyzerException) {
|
||||||
catch (e: AnalyzerException) {
|
|
||||||
throw AnalyzerException(e.node, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e)
|
throw AnalyzerException(e.node, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e)
|
||||||
}
|
} catch (e: Exception) {
|
||||||
catch (e: Exception) {
|
|
||||||
throw AnalyzerException(insnNode, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e)
|
throw AnalyzerException(insnNode, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +156,7 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
||||||
frames[instructions.indexOf(insn)]
|
frames[instructions.indexOf(insn)]
|
||||||
|
|
||||||
private fun checkAssertions() {
|
private fun checkAssertions() {
|
||||||
if (instructions.toArray().any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
|
if (instructions.toArray().any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
|
||||||
@@ -250,12 +248,12 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
private fun mergeControlFlowEdge(insn: Int, frame: Frame<V>) {
|
private fun mergeControlFlowEdge(insn: Int, frame: Frame<V>) {
|
||||||
val oldFrame = frames[insn]
|
val oldFrame = frames[insn]
|
||||||
val changes =
|
val changes =
|
||||||
if (oldFrame != null)
|
if (oldFrame != null)
|
||||||
oldFrame.merge(frame, interpreter)
|
oldFrame.merge(frame, interpreter)
|
||||||
else {
|
else {
|
||||||
frames[insn] = newFrame(frame)
|
frames[insn] = newFrame(frame)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
if (changes && !queued[insn]) {
|
if (changes && !queued[insn]) {
|
||||||
queued[insn] = true
|
queued[insn] = true
|
||||||
queue[top++] = insn
|
queue[top++] = insn
|
||||||
|
|||||||
+38
-39
@@ -23,61 +23,60 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
|||||||
|
|
||||||
abstract class ReferenceTrackingInterpreter : OptimizationBasicInterpreter() {
|
abstract class ReferenceTrackingInterpreter : OptimizationBasicInterpreter() {
|
||||||
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
||||||
when {
|
when {
|
||||||
v is ProperTrackedReferenceValue && w is ProperTrackedReferenceValue ->
|
v is ProperTrackedReferenceValue && w is ProperTrackedReferenceValue ->
|
||||||
if (v.descriptor == w.descriptor)
|
if (v.descriptor == w.descriptor)
|
||||||
v
|
v
|
||||||
else
|
else
|
||||||
createTaintedValue(v, w)
|
|
||||||
|
|
||||||
v is TrackedReferenceValue && w is TrackedReferenceValue ->
|
|
||||||
createPossiblyMergedValue(v, w)
|
|
||||||
|
|
||||||
v is TrackedReferenceValue || w is TrackedReferenceValue ->
|
|
||||||
createTaintedValue(v, w)
|
createTaintedValue(v, w)
|
||||||
|
|
||||||
else ->
|
v is TrackedReferenceValue && w is TrackedReferenceValue ->
|
||||||
super.merge(v, w)
|
createPossiblyMergedValue(v, w)
|
||||||
}
|
|
||||||
|
|
||||||
protected fun createTaintedValue(v: BasicValue, w: BasicValue) : TrackedReferenceValue =
|
v is TrackedReferenceValue || w is TrackedReferenceValue ->
|
||||||
TaintedTrackedReferenceValue(
|
createTaintedValue(v, w)
|
||||||
getMergedValueType(v.type, w.type),
|
|
||||||
mergeDescriptors(v, w).also {
|
else ->
|
||||||
assert(it.isNotEmpty()) { "At least one of ($v, $w) should be a tracked reference" }
|
super.merge(v, w)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
protected fun createTaintedValue(v: BasicValue, w: BasicValue): TrackedReferenceValue =
|
||||||
|
TaintedTrackedReferenceValue(
|
||||||
|
getMergedValueType(v.type, w.type),
|
||||||
|
mergeDescriptors(v, w).also {
|
||||||
|
assert(it.isNotEmpty()) { "At least one of ($v, $w) should be a tracked reference" }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
protected fun createMergedValue(v: TrackedReferenceValue, w: TrackedReferenceValue): TrackedReferenceValue =
|
protected fun createMergedValue(v: TrackedReferenceValue, w: TrackedReferenceValue): TrackedReferenceValue =
|
||||||
if (v is TaintedTrackedReferenceValue || w is TaintedTrackedReferenceValue)
|
if (v is TaintedTrackedReferenceValue || w is TaintedTrackedReferenceValue)
|
||||||
createTaintedValue(v, w)
|
createTaintedValue(v, w)
|
||||||
else
|
else
|
||||||
MergedTrackedReferenceValue(getMergedValueType(v.type, w.type), mergeDescriptors(v, w))
|
MergedTrackedReferenceValue(getMergedValueType(v.type, w.type), mergeDescriptors(v, w))
|
||||||
|
|
||||||
protected open fun createPossiblyMergedValue(v: TrackedReferenceValue, w: TrackedReferenceValue): TrackedReferenceValue =
|
protected open fun createPossiblyMergedValue(v: TrackedReferenceValue, w: TrackedReferenceValue): TrackedReferenceValue =
|
||||||
createTaintedValue(v, w)
|
createTaintedValue(v, w)
|
||||||
|
|
||||||
private fun mergeDescriptors(v: BasicValue, w: BasicValue) =
|
private fun mergeDescriptors(v: BasicValue, w: BasicValue) =
|
||||||
v.referenceValueDescriptors + w.referenceValueDescriptors
|
v.referenceValueDescriptors + w.referenceValueDescriptors
|
||||||
|
|
||||||
private val BasicValue.referenceValueDescriptors: Set<ReferenceValueDescriptor>
|
private val BasicValue.referenceValueDescriptors: Set<ReferenceValueDescriptor>
|
||||||
get() = if (this is TrackedReferenceValue) this.descriptors else emptySet()
|
get() = if (this is TrackedReferenceValue) this.descriptors else emptySet()
|
||||||
|
|
||||||
protected fun getMergedValueType(type1: Type?, type2: Type?): Type =
|
protected fun getMergedValueType(type1: Type?, type2: Type?): Type =
|
||||||
when {
|
when {
|
||||||
type1 == null || type2 == null -> AsmTypes.OBJECT_TYPE
|
type1 == null || type2 == null -> AsmTypes.OBJECT_TYPE
|
||||||
type1 == type2 -> type1
|
type1 == type2 -> type1
|
||||||
else -> AsmTypes.OBJECT_TYPE
|
else -> AsmTypes.OBJECT_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
|
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
|
||||||
if (value is TrackedReferenceValue) {
|
if (value is TrackedReferenceValue) {
|
||||||
checkRefValuesUsages(insn, listOf(value))
|
checkRefValuesUsages(insn, listOf(value))
|
||||||
value
|
value
|
||||||
}
|
} else {
|
||||||
else {
|
super.copyOperation(insn, value)
|
||||||
super.copyOperation(insn, value)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
|
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
|
||||||
checkRefValuesUsages(insn, listOf(value))
|
checkRefValuesUsages(insn, listOf(value))
|
||||||
|
|||||||
+14
-13
@@ -22,7 +22,7 @@ interface ReferenceValueDescriptor {
|
|||||||
fun onUseAsTainted()
|
fun onUseAsTainted()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class TrackedReferenceValue(type: Type): StrictBasicValue(type) {
|
sealed class TrackedReferenceValue(type: Type) : StrictBasicValue(type) {
|
||||||
abstract val descriptors: Set<ReferenceValueDescriptor>
|
abstract val descriptors: Set<ReferenceValueDescriptor>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,37 +31,38 @@ class ProperTrackedReferenceValue(type: Type, val descriptor: ReferenceValueDesc
|
|||||||
get() = setOf(descriptor)
|
get() = setOf(descriptor)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
other === this ||
|
other === this ||
|
||||||
other is ProperTrackedReferenceValue && other.descriptor == this.descriptor
|
other is ProperTrackedReferenceValue && other.descriptor == this.descriptor
|
||||||
|
|
||||||
override fun hashCode(): Int =
|
override fun hashCode(): Int =
|
||||||
descriptor.hashCode()
|
descriptor.hashCode()
|
||||||
|
|
||||||
override fun toString(): String =
|
override fun toString(): String =
|
||||||
"[$descriptor]"
|
"[$descriptor]"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class MergedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) {
|
class MergedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) {
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
other === this ||
|
other === this ||
|
||||||
other is MergedTrackedReferenceValue && other.descriptors == this.descriptors
|
other is MergedTrackedReferenceValue && other.descriptors == this.descriptors
|
||||||
|
|
||||||
override fun hashCode(): Int =
|
override fun hashCode(): Int =
|
||||||
descriptors.hashCode()
|
descriptors.hashCode()
|
||||||
|
|
||||||
override fun toString(): String =
|
override fun toString(): String =
|
||||||
descriptors.toString()
|
descriptors.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TaintedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) {
|
class TaintedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) {
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
other === this ||
|
other === this ||
|
||||||
other is TaintedTrackedReferenceValue && other.descriptors == this.descriptors
|
other is TaintedTrackedReferenceValue && other.descriptors == this.descriptors
|
||||||
|
|
||||||
override fun hashCode(): Int =
|
override fun hashCode(): Int =
|
||||||
descriptors.hashCode()
|
descriptors.hashCode()
|
||||||
|
|
||||||
override fun toString(): String =
|
override fun toString(): String =
|
||||||
"!$descriptors"
|
"!$descriptors"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ import org.jetbrains.org.objectweb.asm.Opcodes.*
|
|||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
|
|
||||||
val AbstractInsnNode.isMeaningful: Boolean get() =
|
val AbstractInsnNode.isMeaningful: Boolean
|
||||||
when (this.type) {
|
get() =
|
||||||
AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME -> false
|
when (this.type) {
|
||||||
else -> true
|
AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME -> false
|
||||||
}
|
else -> true
|
||||||
|
}
|
||||||
|
|
||||||
class InsnSequence(val from: AbstractInsnNode, val to: AbstractInsnNode?) : Sequence<AbstractInsnNode> {
|
class InsnSequence(val from: AbstractInsnNode, val to: AbstractInsnNode?) : Sequence<AbstractInsnNode> {
|
||||||
constructor(insnList: InsnList) : this(insnList.first, null)
|
constructor(insnList: InsnList) : this(insnList.first, null)
|
||||||
@@ -44,6 +45,7 @@ class InsnSequence(val from: AbstractInsnNode, val to: AbstractInsnNode?) : Sequ
|
|||||||
current = current!!.next
|
current = current!!.next
|
||||||
return result!!
|
return result!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasNext() = current != to
|
override fun hasNext() = current != to
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,13 +76,15 @@ fun MethodNode.prepareForEmitting() {
|
|||||||
current = prev
|
current = prev
|
||||||
}
|
}
|
||||||
maxStack = -1
|
maxStack = -1
|
||||||
accept(MaxStackFrameSizeAndLocalsCalculator(
|
accept(
|
||||||
|
MaxStackFrameSizeAndLocalsCalculator(
|
||||||
Opcodes.ASM5, access, desc,
|
Opcodes.ASM5, access, desc,
|
||||||
object : MethodVisitor(Opcodes.ASM5) {
|
object : MethodVisitor(Opcodes.ASM5) {
|
||||||
override fun visitMaxs(maxStack: Int, maxLocals: Int) {
|
override fun visitMaxs(maxStack: Int, maxLocals: Int) {
|
||||||
this@prepareForEmitting.maxStack = maxStack
|
this@prepareForEmitting.maxStack = maxStack
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MethodNode.stripOptimizationMarkers() {
|
fun MethodNode.stripOptimizationMarkers() {
|
||||||
@@ -88,15 +92,14 @@ fun MethodNode.stripOptimizationMarkers() {
|
|||||||
while (insn != null) {
|
while (insn != null) {
|
||||||
if (isOptimizationMarker(insn)) {
|
if (isOptimizationMarker(insn)) {
|
||||||
insn = instructions.removeNodeGetNext(insn)
|
insn = instructions.removeNodeGetNext(insn)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
insn = insn.next
|
insn = insn.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isOptimizationMarker(insn: AbstractInsnNode) =
|
private fun isOptimizationMarker(insn: AbstractInsnNode) =
|
||||||
PseudoInsn.STORE_NOT_NULL.isa(insn)
|
PseudoInsn.STORE_NOT_NULL.isa(insn)
|
||||||
|
|
||||||
fun MethodNode.removeEmptyCatchBlocks() {
|
fun MethodNode.removeEmptyCatchBlocks() {
|
||||||
tryCatchBlocks = tryCatchBlocks.filter { tcb ->
|
tryCatchBlocks = tryCatchBlocks.filter { tcb ->
|
||||||
@@ -143,7 +146,7 @@ fun MethodNode.removeUnusedLocalVariables() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun VarInsnNode.isSize2LoadStoreOperation() =
|
private fun VarInsnNode.isSize2LoadStoreOperation() =
|
||||||
opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE
|
opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE
|
||||||
|
|
||||||
fun MethodNode.remapLocalVariables(remapping: IntArray) {
|
fun MethodNode.remapLocalVariables(remapping: IntArray) {
|
||||||
for (insn in instructions.toArray()) {
|
for (insn in instructions.toArray()) {
|
||||||
@@ -177,7 +180,7 @@ inline fun AbstractInsnNode.findPreviousOrNull(predicate: (AbstractInsnNode) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.hasOpcode(): Boolean =
|
fun AbstractInsnNode.hasOpcode(): Boolean =
|
||||||
opcode >= 0
|
opcode >= 0
|
||||||
|
|
||||||
// See InstructionAdapter
|
// See InstructionAdapter
|
||||||
//
|
//
|
||||||
@@ -192,27 +195,29 @@ fun AbstractInsnNode.hasOpcode(): Boolean =
|
|||||||
// mv.visitLdcInsn(new Integer(cst));
|
// mv.visitLdcInsn(new Integer(cst));
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
val AbstractInsnNode.intConstant: Int? get() =
|
val AbstractInsnNode.intConstant: Int?
|
||||||
when (opcode) {
|
get() =
|
||||||
in ICONST_M1..ICONST_5 -> opcode - ICONST_0
|
when (opcode) {
|
||||||
BIPUSH, SIPUSH -> (this as IntInsnNode).operand
|
in ICONST_M1..ICONST_5 -> opcode - ICONST_0
|
||||||
LDC -> (this as LdcInsnNode).cst as? Int
|
BIPUSH, SIPUSH -> (this as IntInsnNode).operand
|
||||||
else -> null
|
LDC -> (this as LdcInsnNode).cst as? Int
|
||||||
}
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEach { add(it) } }
|
fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEach { add(it) } }
|
||||||
|
|
||||||
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE
|
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE
|
||||||
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD
|
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD
|
||||||
|
|
||||||
val AbstractInsnNode?.debugText get() =
|
val AbstractInsnNode?.debugText
|
||||||
|
get() =
|
||||||
if (this == null) "<null>" else "${this::class.java.simpleName}: $insnText"
|
if (this == null) "<null>" else "${this::class.java.simpleName}: $insnText"
|
||||||
|
|
||||||
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcode: Int, condition: T.() -> Boolean): Boolean =
|
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcode: Int, condition: T.() -> Boolean): Boolean =
|
||||||
takeInsnIf(opcode, condition) != null
|
takeInsnIf(opcode, condition) != null
|
||||||
|
|
||||||
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
|
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
|
||||||
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
|
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
|
||||||
|
|
||||||
fun InsnList.removeAll(nodes: Collection<AbstractInsnNode>) {
|
fun InsnList.removeAll(nodes: Collection<AbstractInsnNode>) {
|
||||||
for (node in nodes) remove(node)
|
for (node in nodes) remove(node)
|
||||||
|
|||||||
+11
-11
@@ -51,13 +51,14 @@ class VariableLivenessFrame(val maxLocals: Int) : VarFrame<VariableLivenessFrame
|
|||||||
|
|
||||||
override fun hashCode() = bitSet.hashCode()
|
override fun hashCode() = bitSet.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun analyzeLiveness(node: MethodNode): List<VariableLivenessFrame> {
|
fun analyzeLiveness(node: MethodNode): List<VariableLivenessFrame> {
|
||||||
val typeAnnotatedFrames = MethodTransformer.analyze("fake", node, OptimizationBasicInterpreter())
|
val typeAnnotatedFrames = MethodTransformer.analyze("fake", node, OptimizationBasicInterpreter())
|
||||||
return analyze(node, object : BackwardAnalysisInterpreter<VariableLivenessFrame> {
|
return analyze(node, object : BackwardAnalysisInterpreter<VariableLivenessFrame> {
|
||||||
override fun newFrame(maxLocals: Int) = VariableLivenessFrame(maxLocals)
|
override fun newFrame(maxLocals: Int) = VariableLivenessFrame(maxLocals)
|
||||||
override fun def(frame: VariableLivenessFrame, insn: AbstractInsnNode) = defVar(frame, insn)
|
override fun def(frame: VariableLivenessFrame, insn: AbstractInsnNode) = defVar(frame, insn)
|
||||||
override fun use(frame: VariableLivenessFrame, insn: AbstractInsnNode) =
|
override fun use(frame: VariableLivenessFrame, insn: AbstractInsnNode) =
|
||||||
useVar(frame, insn, node, typeAnnotatedFrames[node.instructions.indexOf(insn)])
|
useVar(frame, insn, node, typeAnnotatedFrames[node.instructions.indexOf(insn)])
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -69,24 +70,23 @@ private fun defVar(frame: VariableLivenessFrame, insn: AbstractInsnNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun useVar(
|
private fun useVar(
|
||||||
frame: VariableLivenessFrame,
|
frame: VariableLivenessFrame,
|
||||||
insn: AbstractInsnNode,
|
insn: AbstractInsnNode,
|
||||||
node: MethodNode,
|
node: MethodNode,
|
||||||
// May be null in case of dead code
|
// May be null in case of dead code
|
||||||
typeAnnotatedFrame: Frame<BasicValue>?
|
typeAnnotatedFrame: Frame<BasicValue>?
|
||||||
) {
|
) {
|
||||||
val index = node.instructions.indexOf(insn)
|
val index = node.instructions.indexOf(insn)
|
||||||
node.localVariables.filter {
|
node.localVariables.filter {
|
||||||
node.instructions.indexOf(it.start) < index && index < node.instructions.indexOf(it.end) &&
|
node.instructions.indexOf(it.start) < index && index < node.instructions.indexOf(it.end) &&
|
||||||
Type.getType(it.desc).sort == typeAnnotatedFrame?.getLocal(it.index)?.type?.sort
|
Type.getType(it.desc).sort == typeAnnotatedFrame?.getLocal(it.index)?.type?.sort
|
||||||
}.forEach {
|
}.forEach {
|
||||||
frame.markAlive(it.index)
|
frame.markAlive(it.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insn is VarInsnNode && insn.isLoadOperation()) {
|
if (insn is VarInsnNode && insn.isLoadOperation()) {
|
||||||
frame.markAlive(insn.`var`)
|
frame.markAlive(insn.`var`)
|
||||||
}
|
} else if (insn is IincInsnNode) {
|
||||||
else if (insn is IincInsnNode) {
|
|
||||||
frame.markAlive(insn.`var`)
|
frame.markAlive(insn.`var`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-8
@@ -30,15 +30,15 @@ import java.util.*
|
|||||||
|
|
||||||
private class DecompiledTryDescriptor(val tryStartLabel: LabelNode) {
|
private class DecompiledTryDescriptor(val tryStartLabel: LabelNode) {
|
||||||
// Only used for assertions
|
// Only used for assertions
|
||||||
var defaultHandlerTcb : TryCatchBlockNode? = null
|
var defaultHandlerTcb: TryCatchBlockNode? = null
|
||||||
val handlerStartLabels = hashSetOf<LabelNode>()
|
val handlerStartLabels = hashSetOf<LabelNode>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TryCatchBlockNode.isDefaultHandlerNode(): Boolean =
|
private fun TryCatchBlockNode.isDefaultHandlerNode(): Boolean =
|
||||||
start == handler
|
start == handler
|
||||||
|
|
||||||
private fun MethodNode.debugString(tcb: TryCatchBlockNode): String =
|
private fun MethodNode.debugString(tcb: TryCatchBlockNode): String =
|
||||||
"TCB<${instructions.indexOf(tcb.start)}, ${instructions.indexOf(tcb.end)}, ${instructions.indexOf(tcb.handler)}>"
|
"TCB<${instructions.indexOf(tcb.start)}, ${instructions.indexOf(tcb.end)}, ${instructions.indexOf(tcb.handler)}>"
|
||||||
|
|
||||||
internal fun insertTryCatchBlocksMarkers(methodNode: MethodNode): Map<AbstractInsnNode, AbstractInsnNode> {
|
internal fun insertTryCatchBlocksMarkers(methodNode: MethodNode): Map<AbstractInsnNode, AbstractInsnNode> {
|
||||||
if (methodNode.tryCatchBlocks.isEmpty()) return emptyMap()
|
if (methodNode.tryCatchBlocks.isEmpty()) return emptyMap()
|
||||||
@@ -64,9 +64,9 @@ private fun transformTryCatchBlocks(methodNode: MethodNode, newTryStartLabels: H
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun insertSaveRestoreStackMarkers(
|
private fun insertSaveRestoreStackMarkers(
|
||||||
decompiledTryDescriptorForStart: Map<LabelNode, DecompiledTryDescriptor>,
|
decompiledTryDescriptorForStart: Map<LabelNode, DecompiledTryDescriptor>,
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
newTryStartLabels: MutableMap<LabelNode, LabelNode>
|
newTryStartLabels: MutableMap<LabelNode, LabelNode>
|
||||||
): Map<AbstractInsnNode, AbstractInsnNode> {
|
): Map<AbstractInsnNode, AbstractInsnNode> {
|
||||||
val restoreStackToSaveMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
|
val restoreStackToSaveMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
|
||||||
val saveStackMarkerByTryLabel = hashMapOf<LabelNode, AbstractInsnNode>()
|
val saveStackMarkerByTryLabel = hashMapOf<LabelNode, AbstractInsnNode>()
|
||||||
@@ -92,8 +92,7 @@ private fun insertSaveRestoreStackMarkers(
|
|||||||
methodNode.instructions.insertBefore(nopNode, saveStackMarker)
|
methodNode.instructions.insertBefore(nopNode, saveStackMarker)
|
||||||
methodNode.instructions.insertBefore(nopNode, newTryStartLabel)
|
methodNode.instructions.insertBefore(nopNode, newTryStartLabel)
|
||||||
methodNode.instructions.insert(nopNode, restoreStackMarker)
|
methodNode.instructions.insert(nopNode, restoreStackMarker)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
saveStackMarker = saveStackMarkerByTryLabel[tryStartLabel]!!
|
saveStackMarker = saveStackMarkerByTryLabel[tryStartLabel]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-15
@@ -24,16 +24,19 @@ import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
||||||
|
|
||||||
internal class FixStackAnalyzer(
|
internal class FixStackAnalyzer(
|
||||||
owner: String,
|
owner: String,
|
||||||
val method: MethodNode,
|
val method: MethodNode,
|
||||||
val context: FixStackContext,
|
val context: FixStackContext,
|
||||||
private val skipBreakContinueGotoEdges: Boolean = true
|
private val skipBreakContinueGotoEdges: Boolean = true
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
// Stack size is always non-negative
|
// Stack size is always non-negative
|
||||||
@@ -79,7 +82,7 @@ internal class FixStackAnalyzer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun newFrame(nLocals: Int, nStack: Int): Frame<BasicValue> =
|
override fun newFrame(nLocals: Int, nStack: Int): Frame<BasicValue> =
|
||||||
FixStackFrame(nLocals, nStack)
|
FixStackFrame(nLocals, nStack)
|
||||||
|
|
||||||
private fun indexOf(node: AbstractInsnNode) = method.instructions.indexOf(node)
|
private fun indexOf(node: AbstractInsnNode) = method.instructions.indexOf(node)
|
||||||
|
|
||||||
@@ -128,8 +131,7 @@ internal class FixStackAnalyzer(
|
|||||||
override fun push(value: BasicValue) {
|
override fun push(value: BasicValue) {
|
||||||
if (super.getStackSize() < maxStackSize) {
|
if (super.getStackSize() < maxStackSize) {
|
||||||
super.push(value)
|
super.push(value)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
extraStack.add(value)
|
extraStack.add(value)
|
||||||
maxExtraStackSize = Math.max(maxExtraStackSize, extraStack.size)
|
maxExtraStackSize = Math.max(maxExtraStackSize, extraStack.size)
|
||||||
}
|
}
|
||||||
@@ -142,8 +144,7 @@ internal class FixStackAnalyzer(
|
|||||||
override fun pop(): BasicValue {
|
override fun pop(): BasicValue {
|
||||||
return if (extraStack.isNotEmpty()) {
|
return if (extraStack.isNotEmpty()) {
|
||||||
extraStack.pop()
|
extraStack.pop()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
super.pop()
|
super.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,8 +152,7 @@ internal class FixStackAnalyzer(
|
|||||||
override fun getStack(i: Int): BasicValue {
|
override fun getStack(i: Int): BasicValue {
|
||||||
return if (i < super.getMaxStackSize()) {
|
return if (i < super.getMaxStackSize()) {
|
||||||
super.getStack(i)
|
super.getStack(i)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
extraStack[i - maxStackSize]
|
extraStack[i - maxStackSize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,8 +176,7 @@ internal class FixStackAnalyzer(
|
|||||||
val savedValues = spilledStacks[beforeInlineMarker]
|
val savedValues = spilledStacks[beforeInlineMarker]
|
||||||
pushAll(savedValues!!)
|
pushAll(savedValues!!)
|
||||||
push(returnValue)
|
push(returnValue)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val savedValues = spilledStacks[beforeInlineMarker]
|
val savedValues = spilledStacks[beforeInlineMarker]
|
||||||
pushAll(savedValues!!)
|
pushAll(savedValues!!)
|
||||||
}
|
}
|
||||||
@@ -197,5 +196,4 @@ internal class FixStackAnalyzer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -91,15 +91,15 @@ internal class FixStackContext(val methodNode: MethodNode) {
|
|||||||
private fun indexOf(node: AbstractInsnNode) = methodNode.instructions.indexOf(node)
|
private fun indexOf(node: AbstractInsnNode) = methodNode.instructions.indexOf(node)
|
||||||
|
|
||||||
fun hasAnyMarkers(): Boolean =
|
fun hasAnyMarkers(): Boolean =
|
||||||
breakContinueGotoNodes.isNotEmpty() ||
|
breakContinueGotoNodes.isNotEmpty() ||
|
||||||
fakeAlwaysTrueIfeqMarkers.isNotEmpty() ||
|
fakeAlwaysTrueIfeqMarkers.isNotEmpty() ||
|
||||||
fakeAlwaysFalseIfeqMarkers.isNotEmpty() ||
|
fakeAlwaysFalseIfeqMarkers.isNotEmpty() ||
|
||||||
isThereAnyTryCatch ||
|
isThereAnyTryCatch ||
|
||||||
openingInlineMethodMarker.isNotEmpty()
|
openingInlineMethodMarker.isNotEmpty()
|
||||||
|
|
||||||
fun isAnalysisRequired(): Boolean =
|
fun isAnalysisRequired(): Boolean =
|
||||||
breakContinueGotoNodes.isNotEmpty() ||
|
breakContinueGotoNodes.isNotEmpty() ||
|
||||||
isThereAnyTryCatch ||
|
isThereAnyTryCatch ||
|
||||||
openingInlineMethodMarker.isNotEmpty()
|
openingInlineMethodMarker.isNotEmpty()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-40
@@ -89,10 +89,10 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transformBreakContinueGotos(
|
private fun transformBreakContinueGotos(
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
fixStackContext: FixStackContext,
|
fixStackContext: FixStackContext,
|
||||||
actions: MutableList<() -> Unit>,
|
actions: MutableList<() -> Unit>,
|
||||||
analyzer: FixStackAnalyzer
|
analyzer: FixStackAnalyzer
|
||||||
) {
|
) {
|
||||||
fixStackContext.breakContinueGotoNodes.forEach { gotoNode ->
|
fixStackContext.breakContinueGotoNodes.forEach { gotoNode ->
|
||||||
val gotoIndex = methodNode.instructions.indexOf(gotoNode)
|
val gotoIndex = methodNode.instructions.indexOf(gotoNode)
|
||||||
@@ -106,13 +106,11 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
"Label at $labelIndex, jump at $gotoIndex: stack underflow: $expectedStackSize > $actualStackSize"
|
"Label at $labelIndex, jump at $gotoIndex: stack underflow: $expectedStackSize > $actualStackSize"
|
||||||
}
|
}
|
||||||
val actualStackContent = analyzer.getActualStack(gotoNode)
|
val actualStackContent = analyzer.getActualStack(gotoNode)
|
||||||
?: throw AssertionError("Jump at $gotoIndex should be alive")
|
?: throw AssertionError("Jump at $gotoIndex should be alive")
|
||||||
actions.add { replaceMarkerWithPops(methodNode, gotoNode.previous, expectedStackSize, actualStackContent) }
|
actions.add { replaceMarkerWithPops(methodNode, gotoNode.previous, expectedStackSize, actualStackContent) }
|
||||||
}
|
} else if (actualStackSize >= 0 && expectedStackSize < 0) {
|
||||||
else if (actualStackSize >= 0 && expectedStackSize < 0) {
|
|
||||||
throw AssertionError("Live jump $gotoIndex to dead label $labelIndex")
|
throw AssertionError("Live jump $gotoIndex to dead label $labelIndex")
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val marker = gotoNode.previous
|
val marker = gotoNode.previous
|
||||||
actions.add { methodNode.instructions.remove(marker) }
|
actions.add { methodNode.instructions.remove(marker) }
|
||||||
}
|
}
|
||||||
@@ -120,10 +118,10 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transformSaveRestoreStackMarkers(
|
private fun transformSaveRestoreStackMarkers(
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
context: FixStackContext,
|
context: FixStackContext,
|
||||||
actions: MutableList<() -> Unit>,
|
actions: MutableList<() -> Unit>,
|
||||||
analyzer: FixStackAnalyzer
|
analyzer: FixStackAnalyzer
|
||||||
) {
|
) {
|
||||||
val localVariablesManager = LocalVariablesManager(context, methodNode)
|
val localVariablesManager = LocalVariablesManager(context, methodNode)
|
||||||
InsnSequence(methodNode.instructions).forEach { marker ->
|
InsnSequence(methodNode.instructions).forEach { marker ->
|
||||||
@@ -142,18 +140,17 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transformSaveStackMarker(
|
private fun transformSaveStackMarker(
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
actions: MutableList<() -> Unit>,
|
actions: MutableList<() -> Unit>,
|
||||||
analyzer: FixStackAnalyzer,
|
analyzer: FixStackAnalyzer,
|
||||||
marker: AbstractInsnNode,
|
marker: AbstractInsnNode,
|
||||||
localVariablesManager: LocalVariablesManager
|
localVariablesManager: LocalVariablesManager
|
||||||
) {
|
) {
|
||||||
val savedStackValues = analyzer.getStackToSpill(marker)
|
val savedStackValues = analyzer.getStackToSpill(marker)
|
||||||
if (savedStackValues != null) {
|
if (savedStackValues != null) {
|
||||||
val savedStackDescriptor = localVariablesManager.allocateVariablesForSaveStackMarker(marker, savedStackValues)
|
val savedStackDescriptor = localVariablesManager.allocateVariablesForSaveStackMarker(marker, savedStackValues)
|
||||||
actions.add { saveStack(methodNode, marker, savedStackDescriptor) }
|
actions.add { saveStack(methodNode, marker, savedStackDescriptor) }
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// marker is dead code
|
// marker is dead code
|
||||||
localVariablesManager.allocateVariablesForSaveStackMarker(marker, emptyList())
|
localVariablesManager.allocateVariablesForSaveStackMarker(marker, emptyList())
|
||||||
actions.add { methodNode.instructions.remove(marker) }
|
actions.add { methodNode.instructions.remove(marker) }
|
||||||
@@ -161,10 +158,10 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transformRestoreStackMarker(
|
private fun transformRestoreStackMarker(
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
actions: MutableList<() -> Unit>,
|
actions: MutableList<() -> Unit>,
|
||||||
marker: AbstractInsnNode,
|
marker: AbstractInsnNode,
|
||||||
localVariablesManager: LocalVariablesManager
|
localVariablesManager: LocalVariablesManager
|
||||||
) {
|
) {
|
||||||
val savedStackDescriptor = localVariablesManager.getSavedStackDescriptor(marker)
|
val savedStackDescriptor = localVariablesManager.getSavedStackDescriptor(marker)
|
||||||
actions.add { restoreStack(methodNode, marker, savedStackDescriptor) }
|
actions.add { restoreStack(methodNode, marker, savedStackDescriptor) }
|
||||||
@@ -172,11 +169,11 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transformAfterInlineCallMarker(
|
private fun transformAfterInlineCallMarker(
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
actions: MutableList<() -> Unit>,
|
actions: MutableList<() -> Unit>,
|
||||||
analyzer: FixStackAnalyzer,
|
analyzer: FixStackAnalyzer,
|
||||||
inlineMarker: AbstractInsnNode,
|
inlineMarker: AbstractInsnNode,
|
||||||
localVariablesManager: LocalVariablesManager
|
localVariablesManager: LocalVariablesManager
|
||||||
) {
|
) {
|
||||||
val savedStackDescriptor = localVariablesManager.getBeforeInlineDescriptor(inlineMarker)
|
val savedStackDescriptor = localVariablesManager.getBeforeInlineDescriptor(inlineMarker)
|
||||||
val stackContentAfterInline = analyzer.getActualStack(inlineMarker)
|
val stackContentAfterInline = analyzer.getActualStack(inlineMarker)
|
||||||
@@ -186,8 +183,9 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
val returnValue = stackContentAfterInline.last()
|
val returnValue = stackContentAfterInline.last()
|
||||||
val returnValueLocalVarIndex = localVariablesManager.createReturnValueVariable(returnValue)
|
val returnValueLocalVarIndex = localVariablesManager.createReturnValueVariable(returnValue)
|
||||||
actions.add {
|
actions.add {
|
||||||
restoreStackWithReturnValue(methodNode, inlineMarker, savedStackDescriptor,
|
restoreStackWithReturnValue(
|
||||||
returnValue, returnValueLocalVarIndex
|
methodNode, inlineMarker, savedStackDescriptor,
|
||||||
|
returnValue, returnValueLocalVarIndex
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,8 +194,7 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
else ->
|
else ->
|
||||||
throw AssertionError("Inline method should not leave more than 1 value on stack")
|
throw AssertionError("Inline method should not leave more than 1 value on stack")
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// after inline marker is dead code
|
// after inline marker is dead code
|
||||||
actions.add { methodNode.instructions.remove(inlineMarker) }
|
actions.add { methodNode.instructions.remove(inlineMarker) }
|
||||||
}
|
}
|
||||||
@@ -205,18 +202,17 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transformBeforeInlineCallMarker(
|
private fun transformBeforeInlineCallMarker(
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
actions: MutableList<() -> Unit>,
|
actions: MutableList<() -> Unit>,
|
||||||
analyzer: FixStackAnalyzer,
|
analyzer: FixStackAnalyzer,
|
||||||
inlineMarker: AbstractInsnNode,
|
inlineMarker: AbstractInsnNode,
|
||||||
localVariablesManager: LocalVariablesManager
|
localVariablesManager: LocalVariablesManager
|
||||||
) {
|
) {
|
||||||
val savedStackValues = analyzer.getStackToSpill(inlineMarker)
|
val savedStackValues = analyzer.getStackToSpill(inlineMarker)
|
||||||
if (savedStackValues != null) {
|
if (savedStackValues != null) {
|
||||||
val savedStackDescriptor = localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, savedStackValues)
|
val savedStackDescriptor = localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, savedStackValues)
|
||||||
actions.add { saveStack(methodNode, inlineMarker, savedStackDescriptor) }
|
actions.add { saveStack(methodNode, inlineMarker, savedStackDescriptor) }
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// before inline marker is dead code
|
// before inline marker is dead code
|
||||||
localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, emptyList())
|
localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, emptyList())
|
||||||
actions.add { methodNode.instructions.remove(inlineMarker) }
|
actions.add { methodNode.instructions.remove(inlineMarker) }
|
||||||
|
|||||||
+13
-6
@@ -23,7 +23,7 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
|||||||
internal class LocalVariablesManager(val context: FixStackContext, val methodNode: MethodNode) {
|
internal class LocalVariablesManager(val context: FixStackContext, val methodNode: MethodNode) {
|
||||||
private class AllocatedHandle(val savedStackDescriptor: SavedStackDescriptor, var numRestoreMarkers: Int) {
|
private class AllocatedHandle(val savedStackDescriptor: SavedStackDescriptor, var numRestoreMarkers: Int) {
|
||||||
fun isFullyEmitted(): Boolean =
|
fun isFullyEmitted(): Boolean =
|
||||||
numRestoreMarkers == 0
|
numRestoreMarkers == 0
|
||||||
|
|
||||||
fun markRestoreNodeEmitted() {
|
fun markRestoreNodeEmitted() {
|
||||||
assert(numRestoreMarkers > 0) { "Emitted more restore markers than expected for $savedStackDescriptor" }
|
assert(numRestoreMarkers > 0) { "Emitted more restore markers than expected for $savedStackDescriptor" }
|
||||||
@@ -43,7 +43,11 @@ internal class LocalVariablesManager(val context: FixStackContext, val methodNod
|
|||||||
return allocateNewHandle(numRestoreStackMarkers, saveStackMarker, savedStackValues)
|
return allocateNewHandle(numRestoreStackMarkers, saveStackMarker, savedStackValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun allocateNewHandle(numRestoreStackMarkers: Int, saveStackMarker: AbstractInsnNode, savedStackValues: List<BasicValue>): SavedStackDescriptor {
|
private fun allocateNewHandle(
|
||||||
|
numRestoreStackMarkers: Int,
|
||||||
|
saveStackMarker: AbstractInsnNode,
|
||||||
|
savedStackValues: List<BasicValue>
|
||||||
|
): SavedStackDescriptor {
|
||||||
if (savedStackValues.any { it.type == null }) {
|
if (savedStackValues.any { it.type == null }) {
|
||||||
throw AssertionError("Uninitialized value on stack at ${methodNode.instructions.indexOf(saveStackMarker)}")
|
throw AssertionError("Uninitialized value on stack at ${methodNode.instructions.indexOf(saveStackMarker)}")
|
||||||
}
|
}
|
||||||
@@ -62,16 +66,19 @@ internal class LocalVariablesManager(val context: FixStackContext, val methodNod
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getFirstUnusedLocalVariableIndex(): Int =
|
private fun getFirstUnusedLocalVariableIndex(): Int =
|
||||||
allocatedHandles.values.fold(initialMaxLocals) {
|
allocatedHandles.values.fold(initialMaxLocals) { index, handle ->
|
||||||
index, handle -> Math.max(index, handle.savedStackDescriptor.firstUnusedLocalVarIndex)
|
Math.max(index, handle.savedStackDescriptor.firstUnusedLocalVarIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun markRestoreStackMarkerEmitted(restoreStackMarker: AbstractInsnNode) {
|
fun markRestoreStackMarkerEmitted(restoreStackMarker: AbstractInsnNode) {
|
||||||
val saveStackMarker = context.saveStackMarkerForRestoreMarker[restoreStackMarker]
|
val saveStackMarker = context.saveStackMarkerForRestoreMarker[restoreStackMarker]
|
||||||
markEmitted(saveStackMarker!!)
|
markEmitted(saveStackMarker!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allocateVariablesForBeforeInlineMarker(beforeInlineMarker: AbstractInsnNode, savedStackValues: List<BasicValue>): SavedStackDescriptor {
|
fun allocateVariablesForBeforeInlineMarker(
|
||||||
|
beforeInlineMarker: AbstractInsnNode,
|
||||||
|
savedStackValues: List<BasicValue>
|
||||||
|
): SavedStackDescriptor {
|
||||||
return allocateNewHandle(1, beforeInlineMarker, savedStackValues)
|
return allocateNewHandle(1, beforeInlineMarker, savedStackValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
-25
@@ -23,10 +23,10 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
||||||
|
|
||||||
fun <V : Value> Frame<V>.top(): V? =
|
fun <V : Value> Frame<V>.top(): V? =
|
||||||
peek(0)
|
peek(0)
|
||||||
|
|
||||||
fun <V : Value> Frame<V>.peek(offset: Int): V? =
|
fun <V : Value> Frame<V>.peek(offset: Int): V? =
|
||||||
if (stackSize > offset) getStack(stackSize - offset - 1) else null
|
if (stackSize > offset) getStack(stackSize - offset - 1) else null
|
||||||
|
|
||||||
private fun <V : Value> Frame<V>.peekWordsTo(dest: MutableList<V>, size: Int, offset0: Int = 0): Int {
|
private fun <V : Value> Frame<V>.peekWordsTo(dest: MutableList<V>, size: Int, offset0: Int = 0): Int {
|
||||||
var offset = offset0
|
var offset = offset0
|
||||||
@@ -54,22 +54,22 @@ fun <V : Value> Frame<V>.peekWords(size1: Int, size2: Int): List<V>? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SavedStackDescriptor(
|
class SavedStackDescriptor(
|
||||||
val savedValues: List<BasicValue>,
|
val savedValues: List<BasicValue>,
|
||||||
val firstLocalVarIndex: Int
|
val firstLocalVarIndex: Int
|
||||||
) {
|
) {
|
||||||
private val savedValuesSize = savedValues.fold(0, { size, value -> size + value.size })
|
private val savedValuesSize = savedValues.fold(0, { size, value -> size + value.size })
|
||||||
val firstUnusedLocalVarIndex = firstLocalVarIndex + savedValuesSize
|
val firstUnusedLocalVarIndex = firstLocalVarIndex + savedValuesSize
|
||||||
|
|
||||||
override fun toString(): String =
|
override fun toString(): String =
|
||||||
"@$firstLocalVarIndex: [$savedValues]"
|
"@$firstLocalVarIndex: [$savedValues]"
|
||||||
|
|
||||||
fun isNotEmpty(): Boolean = savedValues.isNotEmpty()
|
fun isNotEmpty(): Boolean = savedValues.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveStack(
|
fun saveStack(
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
nodeToReplace: AbstractInsnNode,
|
nodeToReplace: AbstractInsnNode,
|
||||||
savedStackDescriptor: SavedStackDescriptor
|
savedStackDescriptor: SavedStackDescriptor
|
||||||
) {
|
) {
|
||||||
with(methodNode.instructions) {
|
with(methodNode.instructions) {
|
||||||
generateStoreInstructions(methodNode, nodeToReplace, savedStackDescriptor)
|
generateStoreInstructions(methodNode, nodeToReplace, savedStackDescriptor)
|
||||||
@@ -85,11 +85,11 @@ fun restoreStack(methodNode: MethodNode, location: AbstractInsnNode, savedStackD
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun restoreStackWithReturnValue(
|
fun restoreStackWithReturnValue(
|
||||||
methodNode: MethodNode,
|
methodNode: MethodNode,
|
||||||
nodeToReplace: AbstractInsnNode,
|
nodeToReplace: AbstractInsnNode,
|
||||||
savedStackDescriptor: SavedStackDescriptor,
|
savedStackDescriptor: SavedStackDescriptor,
|
||||||
returnValue: BasicValue,
|
returnValue: BasicValue,
|
||||||
returnValueLocalVarIndex: Int
|
returnValueLocalVarIndex: Int
|
||||||
) {
|
) {
|
||||||
with(methodNode.instructions) {
|
with(methodNode.instructions) {
|
||||||
insertBefore(nodeToReplace, VarInsnNode(returnValue.type.getOpcode(Opcodes.ISTORE), returnValueLocalVarIndex))
|
insertBefore(nodeToReplace, VarInsnNode(returnValue.type.getOpcode(Opcodes.ISTORE), returnValueLocalVarIndex))
|
||||||
@@ -102,8 +102,10 @@ fun restoreStackWithReturnValue(
|
|||||||
fun generateLoadInstructions(methodNode: MethodNode, location: AbstractInsnNode, savedStackDescriptor: SavedStackDescriptor) {
|
fun generateLoadInstructions(methodNode: MethodNode, location: AbstractInsnNode, savedStackDescriptor: SavedStackDescriptor) {
|
||||||
var localVarIndex = savedStackDescriptor.firstLocalVarIndex
|
var localVarIndex = savedStackDescriptor.firstLocalVarIndex
|
||||||
for (value in savedStackDescriptor.savedValues) {
|
for (value in savedStackDescriptor.savedValues) {
|
||||||
methodNode.instructions.insertBefore(location,
|
methodNode.instructions.insertBefore(
|
||||||
VarInsnNode(value.type.getOpcode(Opcodes.ILOAD), localVarIndex))
|
location,
|
||||||
|
VarInsnNode(value.type.getOpcode(Opcodes.ILOAD), localVarIndex)
|
||||||
|
)
|
||||||
localVarIndex += value.size
|
localVarIndex += value.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,27 +114,31 @@ fun generateStoreInstructions(methodNode: MethodNode, location: AbstractInsnNode
|
|||||||
var localVarIndex = savedStackDescriptor.firstUnusedLocalVarIndex
|
var localVarIndex = savedStackDescriptor.firstUnusedLocalVarIndex
|
||||||
for (value in savedStackDescriptor.savedValues.asReversed()) {
|
for (value in savedStackDescriptor.savedValues.asReversed()) {
|
||||||
localVarIndex -= value.size
|
localVarIndex -= value.size
|
||||||
methodNode.instructions.insertBefore(location,
|
methodNode.instructions.insertBefore(
|
||||||
VarInsnNode(value.type.getOpcode(Opcodes.ISTORE), localVarIndex))
|
location,
|
||||||
|
VarInsnNode(value.type.getOpcode(Opcodes.ISTORE), localVarIndex)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPopInstruction(top: BasicValue) =
|
fun getPopInstruction(top: BasicValue) =
|
||||||
InsnNode(when (top.size) {
|
InsnNode(
|
||||||
1 -> Opcodes.POP
|
when (top.size) {
|
||||||
2 -> Opcodes.POP2
|
1 -> Opcodes.POP
|
||||||
else -> throw AssertionError("Unexpected value type size")
|
2 -> Opcodes.POP2
|
||||||
})
|
else -> throw AssertionError("Unexpected value type size")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
fun removeAlwaysFalseIfeq(methodNode: MethodNode, node: AbstractInsnNode) {
|
fun removeAlwaysFalseIfeq(methodNode: MethodNode, node: AbstractInsnNode) {
|
||||||
with (methodNode.instructions) {
|
with(methodNode.instructions) {
|
||||||
remove(node.next)
|
remove(node.next)
|
||||||
remove(node)
|
remove(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun replaceAlwaysTrueIfeqWithGoto(methodNode: MethodNode, node: AbstractInsnNode) {
|
fun replaceAlwaysTrueIfeqWithGoto(methodNode: MethodNode, node: AbstractInsnNode) {
|
||||||
with (methodNode.instructions) {
|
with(methodNode.instructions) {
|
||||||
val next = node.next as JumpInsnNode
|
val next = node.next as JumpInsnNode
|
||||||
insertBefore(node, JumpInsnNode(Opcodes.GOTO, next.label))
|
insertBefore(node, JumpInsnNode(Opcodes.GOTO, next.label))
|
||||||
remove(node)
|
remove(node)
|
||||||
@@ -141,7 +147,7 @@ fun replaceAlwaysTrueIfeqWithGoto(methodNode: MethodNode, node: AbstractInsnNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun replaceMarkerWithPops(methodNode: MethodNode, node: AbstractInsnNode, expectedStackSize: Int, stackContent: List<BasicValue>) {
|
fun replaceMarkerWithPops(methodNode: MethodNode, node: AbstractInsnNode, expectedStackSize: Int, stackContent: List<BasicValue>) {
|
||||||
with (methodNode.instructions) {
|
with(methodNode.instructions) {
|
||||||
for (stackValue in stackContent.subList(expectedStackSize, stackContent.size)) {
|
for (stackValue in stackContent.subList(expectedStackSize, stackContent.size)) {
|
||||||
insert(node, getPopInstruction(stackValue))
|
insert(node, getPopInstruction(stackValue))
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -47,7 +47,7 @@ class NullabilityInterpreter : OptimizationBasicInterpreter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Type?.isReferenceType() =
|
private fun Type?.isReferenceType() =
|
||||||
this?.sort.let { it == Type.OBJECT || it == Type.ARRAY }
|
this?.sort.let { it == Type.OBJECT || it == Type.ARRAY }
|
||||||
|
|
||||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||||
val defaultResult = super.unaryOperation(insn, value)
|
val defaultResult = super.unaryOperation(insn, value)
|
||||||
@@ -82,29 +82,29 @@ class NullabilityInterpreter : OptimizationBasicInterpreter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
||||||
when {
|
when {
|
||||||
v is NullBasicValue && w is NullBasicValue ->
|
v is NullBasicValue && w is NullBasicValue ->
|
||||||
NullBasicValue
|
NullBasicValue
|
||||||
v is NullBasicValue || w is NullBasicValue ->
|
v is NullBasicValue || w is NullBasicValue ->
|
||||||
StrictBasicValue.REFERENCE_VALUE
|
StrictBasicValue.REFERENCE_VALUE
|
||||||
v is ProgressionIteratorBasicValue && w is ProgressionIteratorBasicValue ->
|
v is ProgressionIteratorBasicValue && w is ProgressionIteratorBasicValue ->
|
||||||
mergeNotNullValuesOfSameKind(v, w)
|
mergeNotNullValuesOfSameKind(v, w)
|
||||||
v is ProgressionIteratorBasicValue && w is NotNullBasicValue ->
|
v is ProgressionIteratorBasicValue && w is NotNullBasicValue ->
|
||||||
NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
||||||
w is ProgressionIteratorBasicValue && v is NotNullBasicValue ->
|
w is ProgressionIteratorBasicValue && v is NotNullBasicValue ->
|
||||||
NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
||||||
v is NotNullBasicValue && w is NotNullBasicValue ->
|
v is NotNullBasicValue && w is NotNullBasicValue ->
|
||||||
mergeNotNullValuesOfSameKind(v, w)
|
mergeNotNullValuesOfSameKind(v, w)
|
||||||
else ->
|
else ->
|
||||||
super.merge(v, w)
|
super.merge(v, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mergeNotNullValuesOfSameKind(v: StrictBasicValue, w: StrictBasicValue) =
|
private fun mergeNotNullValuesOfSameKind(v: StrictBasicValue, w: StrictBasicValue) =
|
||||||
if (v.type == w.type) v else NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
if (v.type == w.type) v else NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun TypeInsnNode.getObjectType(): Type =
|
fun TypeInsnNode.getObjectType(): Type =
|
||||||
Type.getObjectType(desc)
|
Type.getObjectType(desc)
|
||||||
|
|
||||||
|
|||||||
+39
-43
@@ -37,7 +37,8 @@ import org.jetbrains.org.objectweb.asm.tree.*
|
|||||||
|
|
||||||
class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
while (TransformerPass(internalClassName, methodNode).run()) {}
|
while (TransformerPass(internalClassName, methodNode).run()) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TransformerPass(val internalClassName: String, val methodNode: MethodNode) {
|
private class TransformerPass(val internalClassName: String, val methodNode: MethodNode) {
|
||||||
@@ -79,10 +80,10 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun AbstractInsnNode.isOptimizable() =
|
private fun AbstractInsnNode.isOptimizable() =
|
||||||
opcode == Opcodes.IFNULL ||
|
opcode == Opcodes.IFNULL ||
|
||||||
opcode == Opcodes.IFNONNULL ||
|
opcode == Opcodes.IFNONNULL ||
|
||||||
opcode == Opcodes.INSTANCEOF ||
|
opcode == Opcodes.INSTANCEOF ||
|
||||||
isCheckExpressionValueIsNotNull()
|
isCheckExpressionValueIsNotNull()
|
||||||
|
|
||||||
private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, StrictBasicValue>) {
|
private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, StrictBasicValue>) {
|
||||||
for ((insn, value) in nullabilityMap) {
|
for ((insn, value) in nullabilityMap) {
|
||||||
@@ -108,8 +109,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
popReferenceValueBefore(insn)
|
popReferenceValueBefore(insn)
|
||||||
if (alwaysTrue) {
|
if (alwaysTrue) {
|
||||||
set(insn, JumpInsnNode(Opcodes.GOTO, insn.label))
|
set(insn, JumpInsnNode(Opcodes.GOTO, insn.label))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
remove(insn)
|
remove(insn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,8 +120,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
if (nullability == Nullability.NULL) {
|
if (nullability == Nullability.NULL) {
|
||||||
changes = true
|
changes = true
|
||||||
transformTrivialInstanceOf(insn, false)
|
transformTrivialInstanceOf(insn, false)
|
||||||
}
|
} else if (nullability == Nullability.NOT_NULL && value.type.internalName == insn.desc) {
|
||||||
else if (nullability == Nullability.NOT_NULL && value.type.internalName == insn.desc) {
|
|
||||||
changes = true
|
changes = true
|
||||||
transformTrivialInstanceOf(insn, true)
|
transformTrivialInstanceOf(insn, true)
|
||||||
}
|
}
|
||||||
@@ -160,8 +159,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
val previous = insn.previous ?: continue@insnLoop
|
val previous = insn.previous ?: continue@insnLoop
|
||||||
if (previous.opcode == Opcodes.ALOAD) {
|
if (previous.opcode == Opcodes.ALOAD) {
|
||||||
addDependentCheck(insn, previous as VarInsnNode)
|
addDependentCheck(insn, previous as VarInsnNode)
|
||||||
}
|
} else if (previous.opcode == Opcodes.DUP) {
|
||||||
else if (previous.opcode == Opcodes.DUP) {
|
|
||||||
val previous2 = previous.previous ?: continue@insnLoop
|
val previous2 = previous.previous ?: continue@insnLoop
|
||||||
if (previous2.opcode == Opcodes.ALOAD) {
|
if (previous2.opcode == Opcodes.ALOAD) {
|
||||||
addDependentCheck(insn, previous2 as VarInsnNode)
|
addDependentCheck(insn, previous2 as VarInsnNode)
|
||||||
@@ -184,8 +182,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
val insn1 = ldcInsn.previous ?: continue@insnLoop
|
val insn1 = ldcInsn.previous ?: continue@insnLoop
|
||||||
if (insn1.opcode == Opcodes.ALOAD) {
|
if (insn1.opcode == Opcodes.ALOAD) {
|
||||||
aLoadInsn = insn1 as VarInsnNode
|
aLoadInsn = insn1 as VarInsnNode
|
||||||
}
|
} else if (insn1.opcode == Opcodes.DUP) {
|
||||||
else if (insn1.opcode == Opcodes.DUP) {
|
|
||||||
val insn2 = insn1.previous ?: continue@insnLoop
|
val insn2 = insn1.previous ?: continue@insnLoop
|
||||||
if (insn2.opcode == Opcodes.ALOAD) {
|
if (insn2.opcode == Opcodes.ALOAD) {
|
||||||
aLoadInsn = insn2 as VarInsnNode
|
aLoadInsn = insn2 as VarInsnNode
|
||||||
@@ -227,7 +224,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
Opcodes.INVOKESTATIC -> {
|
Opcodes.INVOKESTATIC -> {
|
||||||
when {
|
when {
|
||||||
insn.isCheckParameterIsNotNull() ||
|
insn.isCheckParameterIsNotNull() ||
|
||||||
insn.isCheckExpressionValueIsNotNull() ->
|
insn.isCheckExpressionValueIsNotNull() ->
|
||||||
injectAssumptionsForNotNullAssertion(varIndex, insn)
|
injectAssumptionsForNotNullAssertion(varIndex, insn)
|
||||||
insn.isPseudo(PseudoInsn.STORE_NOT_NULL) ->
|
insn.isPseudo(PseudoInsn.STORE_NOT_NULL) ->
|
||||||
injectCodeForStoreNotNull(insn)
|
injectCodeForStoreNotNull(insn)
|
||||||
@@ -318,8 +315,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
methodNode.instructions.add(newLabel)
|
methodNode.instructions.add(newLabel)
|
||||||
next.label = newLabel
|
next.label = newLabel
|
||||||
insertAfterNotNull = newLabel
|
insertAfterNotNull = newLabel
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
originalLabel = null
|
originalLabel = null
|
||||||
insertAfterNotNull = next
|
insertAfterNotNull = next
|
||||||
}
|
}
|
||||||
@@ -395,41 +391,41 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun AbstractInsnNode.isInstanceOfOrNullCheck() =
|
internal fun AbstractInsnNode.isInstanceOfOrNullCheck() =
|
||||||
opcode == Opcodes.INSTANCEOF ||
|
opcode == Opcodes.INSTANCEOF ||
|
||||||
opcode == Opcodes.IFNULL ||
|
opcode == Opcodes.IFNULL ||
|
||||||
opcode == Opcodes.IFNONNULL
|
opcode == Opcodes.IFNONNULL
|
||||||
|
|
||||||
internal fun AbstractInsnNode.isCheckParameterIsNotNull() =
|
internal fun AbstractInsnNode.isCheckParameterIsNotNull() =
|
||||||
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
||||||
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
||||||
name == "checkParameterIsNotNull" &&
|
name == "checkParameterIsNotNull" &&
|
||||||
desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
|
desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun AbstractInsnNode.isCheckExpressionValueIsNotNull() =
|
internal fun AbstractInsnNode.isCheckExpressionValueIsNotNull() =
|
||||||
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
||||||
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
||||||
name == "checkExpressionValueIsNotNull" &&
|
name == "checkExpressionValueIsNotNull" &&
|
||||||
desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
|
desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun AbstractInsnNode.isThrowIntrinsic() =
|
internal fun AbstractInsnNode.isThrowIntrinsic() =
|
||||||
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
||||||
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
||||||
name in THROW_INTRINSIC_METHOD_NAMES
|
name in THROW_INTRINSIC_METHOD_NAMES
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val THROW_INTRINSIC_METHOD_NAMES =
|
internal val THROW_INTRINSIC_METHOD_NAMES =
|
||||||
setOf(
|
setOf(
|
||||||
"throwNpe",
|
"throwNpe",
|
||||||
"throwUninitializedProperty",
|
"throwUninitializedProperty",
|
||||||
"throwUninitializedPropertyAccessException",
|
"throwUninitializedPropertyAccessException",
|
||||||
"throwAssert",
|
"throwAssert",
|
||||||
"throwIllegalArgument",
|
"throwIllegalArgument",
|
||||||
"throwIllegalState",
|
"throwIllegalState",
|
||||||
"throwParameterIsNullException",
|
"throwParameterIsNullException",
|
||||||
"throwUndefinedForReified"
|
"throwUndefinedForReified"
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun InsnList.popReferenceValueBefore(insn: AbstractInsnNode) {
|
internal fun InsnList.popReferenceValueBefore(insn: AbstractInsnNode) {
|
||||||
val prev = insn.previous
|
val prev = insn.previous
|
||||||
|
|||||||
+7
-6
@@ -37,14 +37,15 @@ object NullBasicValue : StrictBasicValue(AsmTypes.OBJECT_TYPE)
|
|||||||
|
|
||||||
enum class Nullability {
|
enum class Nullability {
|
||||||
NULL, NOT_NULL, NULLABLE;
|
NULL, NOT_NULL, NULLABLE;
|
||||||
|
|
||||||
fun isNull() = this == NULL
|
fun isNull() = this == NULL
|
||||||
fun isNotNull() = this == NOT_NULL
|
fun isNotNull() = this == NOT_NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BasicValue.getNullability(): Nullability =
|
fun BasicValue.getNullability(): Nullability =
|
||||||
when (this) {
|
when (this) {
|
||||||
is NullBasicValue -> Nullability.NULL
|
is NullBasicValue -> Nullability.NULL
|
||||||
is NotNullBasicValue -> Nullability.NOT_NULL
|
is NotNullBasicValue -> Nullability.NOT_NULL
|
||||||
is ProgressionIteratorBasicValue -> Nullability.NOT_NULL
|
is ProgressionIteratorBasicValue -> Nullability.NOT_NULL
|
||||||
else -> Nullability.NULLABLE
|
else -> Nullability.NULLABLE
|
||||||
}
|
}
|
||||||
+1
-1
@@ -27,6 +27,6 @@ open class CompositeMethodTransformer(private val transformers: List<MethodTrans
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
inline fun build(builder: MutableList<MethodTransformer>.() -> Unit) =
|
inline fun build(builder: MutableList<MethodTransformer>.() -> Unit) =
|
||||||
CompositeMethodTransformer(ArrayList<MethodTransformer>().apply { builder() })
|
CompositeMethodTransformer(ArrayList<MethodTransformer>().apply { builder() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user