Converting optimization.boxing to Kotlin: convert & simplify
This commit is contained in:
+55
-102
@@ -14,121 +14,74 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.optimization.boxing;
|
package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||||
|
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil;
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList
|
||||||
import java.util.HashSet;
|
import java.util.HashSet
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class BoxedBasicValue extends BasicValue {
|
class BoxedBasicValue(
|
||||||
private final Set<AbstractInsnNode> associatedInsns = new HashSet<AbstractInsnNode>();
|
boxedType: Type,
|
||||||
private final Set<Pair<AbstractInsnNode, Type>> unboxingWithCastInsns = new HashSet<Pair<AbstractInsnNode, Type>>();
|
val boxingInsn: AbstractInsnNode,
|
||||||
private final AbstractInsnNode boxingInsn;
|
val progressionIterator: ProgressionIteratorBasicValue?
|
||||||
private final Set<Integer> associatedVariables = new HashSet<Integer>();
|
) : BasicValue(boxedType) {
|
||||||
private final Set<BoxedBasicValue> mergedWith = new HashSet<BoxedBasicValue>();
|
private val associatedInsns = HashSet<AbstractInsnNode>()
|
||||||
private final Type primitiveType;
|
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
|
||||||
private final ProgressionIteratorBasicValue progressionIterator;
|
private val associatedVariables = HashSet<Int>()
|
||||||
private boolean isSafeToRemove = true;
|
private val mergedWith = HashSet<BoxedBasicValue>()
|
||||||
|
|
||||||
public BoxedBasicValue(
|
val primitiveType: Type = AsmUtil.unboxType(boxedType)
|
||||||
@NotNull Type boxedType,
|
var isSafeToRemove = true; private set
|
||||||
@NotNull AbstractInsnNode boxingInsn,
|
|
||||||
@Nullable ProgressionIteratorBasicValue progressionIterator
|
override fun equals(other: Any?) =
|
||||||
) {
|
this === other
|
||||||
super(boxedType);
|
|
||||||
this.primitiveType = AsmUtil.unboxType(boxedType);
|
fun typeEquals(other: BasicValue) =
|
||||||
this.boxingInsn = boxingInsn;
|
other is BoxedBasicValue && type == other.type
|
||||||
this.progressionIterator = progressionIterator;
|
|
||||||
|
override fun hashCode() =
|
||||||
|
System.identityHashCode(this)
|
||||||
|
|
||||||
|
fun getAssociatedInsns(): List<AbstractInsnNode> =
|
||||||
|
ArrayList(associatedInsns)
|
||||||
|
|
||||||
|
fun addInsn(insnNode: AbstractInsnNode) {
|
||||||
|
associatedInsns.add(insnNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
fun addVariableIndex(index: Int) {
|
||||||
public boolean equals(Object o) {
|
associatedVariables.add(index)
|
||||||
return this == o;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean typeEquals(Object o) {
|
fun getVariablesIndexes(): List<Int> =
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
ArrayList(associatedVariables)
|
||||||
|
|
||||||
BoxedBasicValue that = (BoxedBasicValue) o;
|
fun addMergedWith(value: BoxedBasicValue) {
|
||||||
|
mergedWith.add(value)
|
||||||
return getType().equals(that.getType());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
fun getMergedWith(): Iterable<BoxedBasicValue> =
|
||||||
public int hashCode() {
|
mergedWith
|
||||||
return System.identityHashCode(this);
|
|
||||||
|
fun markAsUnsafeToRemove() {
|
||||||
|
isSafeToRemove = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AbstractInsnNode> getAssociatedInsns() {
|
fun isDoubleSize() =
|
||||||
return new ArrayList<AbstractInsnNode>(associatedInsns);
|
primitiveType.size == 2
|
||||||
|
|
||||||
|
fun isFromProgressionIterator() =
|
||||||
|
progressionIterator != null
|
||||||
|
|
||||||
|
fun addUnboxingWithCastTo(insn: AbstractInsnNode, type: Type) {
|
||||||
|
unboxingWithCastInsns.add(Pair.create(insn, type))
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addInsn(AbstractInsnNode insnNode) {
|
fun getUnboxingWithCastInsns(): Set<Pair<AbstractInsnNode, Type>> =
|
||||||
associatedInsns.add(insnNode);
|
unboxingWithCastInsns
|
||||||
}
|
|
||||||
|
|
||||||
public void addVariableIndex(int index) {
|
|
||||||
associatedVariables.add(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Integer> getVariablesIndexes() {
|
|
||||||
return new ArrayList<Integer>(associatedVariables);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getPrimitiveType() {
|
|
||||||
return primitiveType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addMergedWith(@NotNull BoxedBasicValue value) {
|
|
||||||
mergedWith.add(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Iterable<BoxedBasicValue> getMergedWith() {
|
|
||||||
return mergedWith;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void markAsUnsafeToRemove() {
|
|
||||||
isSafeToRemove = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSafeToRemove() {
|
|
||||||
return isSafeToRemove;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDoubleSize() {
|
|
||||||
return getPrimitiveType().getSize() == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public AbstractInsnNode getBoxingInsn() {
|
|
||||||
return boxingInsn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFromProgressionIterator() {
|
|
||||||
return progressionIterator != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public ProgressionIteratorBasicValue getProgressionIterator() {
|
|
||||||
return progressionIterator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addUnboxingWithCastTo(@NotNull AbstractInsnNode insn, @NotNull Type type) {
|
|
||||||
unboxingWithCastInsns.add(Pair.create(insn, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Set<Pair<AbstractInsnNode, Type>> getUnboxingWithCastInsns() {
|
|
||||||
return unboxingWithCastInsns;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+125
-208
@@ -16,237 +16,154 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.optimization.boxing;
|
package org.jetbrains.kotlin.codegen.optimization.boxing;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.kotlin.codegen.RangeCodegenUtil
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil;
|
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||||
import org.jetbrains.kotlin.codegen.RangeCodegenUtil;
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter;
|
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException
|
||||||
import org.jetbrains.org.objectweb.asm.tree.InsnList;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode;
|
import java.util.*
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasicInterpreter() {
|
||||||
import java.util.List;
|
private val boxingPlaces = HashMap<Int, BoxedBasicValue>()
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
protected open fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?): BasicValue {
|
||||||
private static final ImmutableSet<String> UNBOXING_METHOD_NAMES;
|
val index = insnList.indexOf(insn)
|
||||||
|
return boxingPlaces.getOrPut(index) {
|
||||||
static {
|
val boxedBasicValue = BoxedBasicValue(type, insn, progressionIterator)
|
||||||
UNBOXING_METHOD_NAMES = ImmutableSet.of(
|
onNewBoxedValue(boxedBasicValue)
|
||||||
"booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue"
|
boxedBasicValue
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(AnalyzerException::class)
|
||||||
|
override fun naryOperation(insn: AbstractInsnNode, values: List<BasicValue>): BasicValue? {
|
||||||
|
val value = super.naryOperation(insn, values)
|
||||||
|
val firstArg = values.firstOrNull() ?: return value
|
||||||
|
|
||||||
private final Map<Integer, BoxedBasicValue> boxingPlaces = new HashMap<Integer, BoxedBasicValue>();
|
return when {
|
||||||
private final InsnList insnList;
|
isBoxing(insn) -> {
|
||||||
|
createNewBoxing(insn, value.type, null)
|
||||||
public BoxingInterpreter(InsnList insnList) {
|
}
|
||||||
this.insnList = insnList;
|
isUnboxing(insn) && firstArg is BoxedBasicValue -> {
|
||||||
}
|
onUnboxing(insn, firstArg, value.type)
|
||||||
|
value
|
||||||
@NotNull
|
}
|
||||||
protected BasicValue createNewBoxing(
|
isIteratorMethodCallOfProgression(insn, values) -> {
|
||||||
@NotNull AbstractInsnNode insn, @NotNull Type type,
|
ProgressionIteratorBasicValue(getValuesTypeOfProgressionClass(firstArg.type.internalName))
|
||||||
@Nullable ProgressionIteratorBasicValue progressionIterator
|
}
|
||||||
) {
|
isNextMethodCallOfProgressionIterator(insn, values) -> {
|
||||||
int index = insnList.indexOf(insn);
|
val progressionIterator = firstArg as? ProgressionIteratorBasicValue
|
||||||
if (!boxingPlaces.containsKey(index)) {
|
?: throw AssertionError("firstArg should be progression iterator")
|
||||||
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(type, insn, progressionIterator);
|
createNewBoxing(insn, AsmUtil.boxType(progressionIterator.valuesPrimitiveType), progressionIterator)
|
||||||
onNewBoxedValue(boxedBasicValue);
|
}
|
||||||
boxingPlaces.put(index, boxedBasicValue);
|
else -> {
|
||||||
}
|
// N-ary operation should be a method call or multinewarray.
|
||||||
|
// Arguments for multinewarray could be only numeric,
|
||||||
return boxingPlaces.get(index);
|
// so if there are boxed values in args, it's not a case of multinewarray.
|
||||||
}
|
for (arg in values) {
|
||||||
|
if (arg is BoxedBasicValue) {
|
||||||
@Override
|
onMethodCallWithBoxedValue(arg)
|
||||||
@Nullable
|
}
|
||||||
public BasicValue naryOperation(@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values) throws AnalyzerException {
|
|
||||||
BasicValue value = super.naryOperation(insn, values);
|
|
||||||
|
|
||||||
if (values.isEmpty()) return value;
|
|
||||||
|
|
||||||
BasicValue firstArg = values.get(0);
|
|
||||||
|
|
||||||
if (isBoxing(insn)) {
|
|
||||||
return createNewBoxing(insn, value.getType(), null);
|
|
||||||
}
|
|
||||||
else if (isUnboxing(insn) &&
|
|
||||||
firstArg instanceof BoxedBasicValue) {
|
|
||||||
onUnboxing(insn, (BoxedBasicValue) firstArg, value.getType());
|
|
||||||
}
|
|
||||||
else if (isIteratorMethodCallOfProgression(insn, values)) {
|
|
||||||
return new ProgressionIteratorBasicValue(
|
|
||||||
getValuesTypeOfProgressionClass(firstArg.getType().getInternalName())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (isNextMethodCallOfProgressionIterator(insn, values)) {
|
|
||||||
assert firstArg instanceof ProgressionIteratorBasicValue : "firstArg should be progression iterator";
|
|
||||||
|
|
||||||
ProgressionIteratorBasicValue progressionIterator = (ProgressionIteratorBasicValue) firstArg;
|
|
||||||
return createNewBoxing(
|
|
||||||
insn,
|
|
||||||
AsmUtil.boxType(progressionIterator.getValuesPrimitiveType()),
|
|
||||||
progressionIterator
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// nary operation should be a method call or multinewarray
|
|
||||||
// arguments for multinewarray could be only numeric
|
|
||||||
// so if there are boxed values in args, it's not a case of multinewarray
|
|
||||||
for (BasicValue arg : values) {
|
|
||||||
if (arg instanceof BoxedBasicValue) {
|
|
||||||
onMethodCallWithBoxedValue((BoxedBasicValue) arg);
|
|
||||||
}
|
}
|
||||||
|
value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isWrapperClassNameOrNumber(@NotNull String internalClassName) {
|
@Throws(AnalyzerException::class)
|
||||||
return isWrapperClassName(internalClassName) || internalClassName.equals(Type.getInternalName(Number.class));
|
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
|
||||||
}
|
if (insn.opcode == Opcodes.CHECKCAST && isExactValue(value))
|
||||||
|
value
|
||||||
|
else
|
||||||
|
super.unaryOperation(insn, value)
|
||||||
|
|
||||||
private static boolean isWrapperClassName(@NotNull String internalClassName) {
|
protected open fun isExactValue(value: BasicValue) =
|
||||||
return JvmPrimitiveType.isWrapperClassName(
|
value is ProgressionIteratorBasicValue ||
|
||||||
buildFqNameByInternal(internalClassName)
|
value is BoxedBasicValue ||
|
||||||
);
|
value.type != null && isProgressionClass(value.type.internalName)
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
override fun merge(v: BasicValue, w: BasicValue) =
|
||||||
private static FqName buildFqNameByInternal(@NotNull String internalClassName) {
|
when {
|
||||||
return new FqName(Type.getObjectType(internalClassName).getClassName());
|
v == BasicValue.UNINITIALIZED_VALUE || w == BasicValue.UNINITIALIZED_VALUE -> {
|
||||||
}
|
BasicValue.UNINITIALIZED_VALUE
|
||||||
|
}
|
||||||
|
v is BoxedBasicValue && v.typeEquals(w) -> {
|
||||||
|
onMergeSuccess(v, w as BoxedBasicValue)
|
||||||
|
v
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (v is BoxedBasicValue) {
|
||||||
|
onMergeFail(v)
|
||||||
|
}
|
||||||
|
if (w is BoxedBasicValue) {
|
||||||
|
onMergeFail(w)
|
||||||
|
}
|
||||||
|
super.merge(v, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isUnboxing(@NotNull AbstractInsnNode insn) {
|
protected open fun onNewBoxedValue(value: BoxedBasicValue) {}
|
||||||
if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) return false;
|
protected open fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {}
|
||||||
|
protected open fun onMethodCallWithBoxedValue(value: BoxedBasicValue) {}
|
||||||
|
protected open fun onMergeFail(value: BoxedBasicValue) {}
|
||||||
|
protected open fun onMergeSuccess(v: BoxedBasicValue, w: BoxedBasicValue) {}
|
||||||
|
|
||||||
MethodInsnNode methodInsn = (MethodInsnNode) insn;
|
companion object {
|
||||||
|
private val UNBOXING_METHOD_NAMES =
|
||||||
|
ImmutableSet.of("booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue")
|
||||||
|
|
||||||
return isWrapperClassNameOrNumber(methodInsn.owner) && isUnboxingMethodName(methodInsn.name);
|
private fun isWrapperClassNameOrNumber(internalClassName: String) =
|
||||||
}
|
isWrapperClassName(internalClassName) || internalClassName == Type.getInternalName(Number::class.java)
|
||||||
|
|
||||||
private static boolean isUnboxingMethodName(@NotNull String name) {
|
private fun isWrapperClassName(internalClassName: String) =
|
||||||
return UNBOXING_METHOD_NAMES.contains(name);
|
JvmPrimitiveType.isWrapperClassName(buildFqNameByInternal(internalClassName))
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isBoxing(@NotNull AbstractInsnNode insn) {
|
private fun buildFqNameByInternal(internalClassName: String) =
|
||||||
if (insn.getOpcode() != Opcodes.INVOKESTATIC) return false;
|
FqName(Type.getObjectType(internalClassName).className)
|
||||||
|
|
||||||
MethodInsnNode node = (MethodInsnNode) insn;
|
private fun isUnboxing(insn: AbstractInsnNode) =
|
||||||
|
insn.opcode == Opcodes.INVOKEVIRTUAL && run {
|
||||||
|
val methodInsn = insn as MethodInsnNode
|
||||||
|
isWrapperClassNameOrNumber(methodInsn.owner) && isUnboxingMethodName(methodInsn.name)
|
||||||
|
}
|
||||||
|
|
||||||
return isWrapperClassName(node.owner) && "valueOf".equals(node.name) &&
|
private fun isUnboxingMethodName(name: String) =
|
||||||
Type.getMethodDescriptor(
|
UNBOXING_METHOD_NAMES.contains(name)
|
||||||
Type.getObjectType(node.owner),
|
|
||||||
AsmUtil.unboxType(Type.getObjectType(node.owner))
|
|
||||||
).equals(node.desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isNextMethodCallOfProgressionIterator(
|
private fun isBoxing(insn: AbstractInsnNode) =
|
||||||
@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values
|
insn.opcode == Opcodes.INVOKESTATIC && run {
|
||||||
) {
|
val methodInsn = insn as MethodInsnNode
|
||||||
return (insn.getOpcode() == Opcodes.INVOKEINTERFACE &&
|
isWrapperClassName(methodInsn.owner) && methodInsn.name == "valueOf" && run {
|
||||||
values.get(0) instanceof ProgressionIteratorBasicValue &&
|
val ownerType = Type.getObjectType(methodInsn.owner)
|
||||||
"next".equals(((MethodInsnNode) insn).name));
|
methodInsn.desc == Type.getMethodDescriptor(ownerType, AsmUtil.unboxType(ownerType))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isIteratorMethodCallOfProgression(
|
private fun isNextMethodCallOfProgressionIterator(insn: AbstractInsnNode, values: kotlin.collections.List<BasicValue>) =
|
||||||
@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values
|
insn.opcode == Opcodes.INVOKEINTERFACE &&
|
||||||
) {
|
values[0] is ProgressionIteratorBasicValue &&
|
||||||
return (insn.getOpcode() == Opcodes.INVOKEINTERFACE &&
|
(insn as MethodInsnNode).name == "next"
|
||||||
values.get(0).getType() != null &&
|
|
||||||
isProgressionClass(values.get(0).getType().getInternalName()) &&
|
|
||||||
"iterator".equals(((MethodInsnNode) insn).name));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isProgressionClass(String internalClassName) {
|
private fun isIteratorMethodCallOfProgression(insn: AbstractInsnNode, values: kotlin.collections.List<BasicValue>) =
|
||||||
return RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName));
|
insn.opcode == Opcodes.INVOKEINTERFACE && run {
|
||||||
}
|
val firstArgType = values[0].type
|
||||||
|
firstArgType != null && isProgressionClass(firstArgType.internalName) && "iterator" == (insn as MethodInsnNode).name
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
private fun isProgressionClass(internalClassName: String) =
|
||||||
* e.g. for "kotlin/IntRange" it returns "Int"
|
RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName))
|
||||||
*
|
|
||||||
* @param progressionClassInternalName
|
|
||||||
* @return
|
|
||||||
* @throws java.lang.AssertionError if progressionClassInternalName is not progression class internal name
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
private static String getValuesTypeOfProgressionClass(String progressionClassInternalName) {
|
|
||||||
PrimitiveType type = RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(
|
|
||||||
buildFqNameByInternal(progressionClassInternalName)
|
|
||||||
);
|
|
||||||
|
|
||||||
assert type != null : "type should be not null";
|
|
||||||
|
|
||||||
return type.getTypeName().asString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
|
||||||
if (insn.getOpcode() == Opcodes.CHECKCAST && isExactValue(value)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.unaryOperation(insn, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isExactValue(@NotNull BasicValue value) {
|
|
||||||
return value instanceof ProgressionIteratorBasicValue ||
|
|
||||||
value instanceof BoxedBasicValue ||
|
|
||||||
(value.getType() != null && isProgressionClass(value.getType().getInternalName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NotNull
|
|
||||||
public BasicValue merge(@NotNull BasicValue v, @NotNull BasicValue w) {
|
|
||||||
if (v == BasicValue.UNINITIALIZED_VALUE || w == BasicValue.UNINITIALIZED_VALUE) {
|
|
||||||
return BasicValue.UNINITIALIZED_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v instanceof BoxedBasicValue && ((BoxedBasicValue) v).typeEquals(w)) {
|
|
||||||
onMergeSuccess((BoxedBasicValue) v, (BoxedBasicValue) w);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v instanceof BoxedBasicValue) {
|
|
||||||
onMergeFail((BoxedBasicValue) v);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (w instanceof BoxedBasicValue) {
|
|
||||||
onMergeFail((BoxedBasicValue) w);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.merge(v, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onNewBoxedValue(@NotNull BoxedBasicValue value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onUnboxing(@NotNull AbstractInsnNode insn, @NotNull BoxedBasicValue value, @NotNull Type resultType) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onMethodCallWithBoxedValue(@NotNull BoxedBasicValue value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onMergeFail(@NotNull BoxedBasicValue value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onMergeSuccess(@NotNull BoxedBasicValue v, @NotNull BoxedBasicValue w) {
|
|
||||||
|
|
||||||
|
private fun getValuesTypeOfProgressionClass(progressionClassInternalName: String) =
|
||||||
|
RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(buildFqNameByInternal(progressionClassInternalName))?.let { type ->
|
||||||
|
type.typeName.asString()
|
||||||
|
} ?: error("type should be not null")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-114
@@ -16,163 +16,129 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.optimization.boxing;
|
package org.jetbrains.kotlin.codegen.optimization.boxing;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.InsnList;
|
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException
|
||||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
|
||||||
|
|
||||||
class RedundantBoxingInterpreter extends BoxingInterpreter {
|
internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterpreter(insnList) {
|
||||||
private static final ImmutableSet<Integer> PERMITTED_OPERATIONS_OPCODES = ImmutableSet.of(
|
|
||||||
Opcodes.ASTORE, Opcodes.ALOAD, Opcodes.POP, Opcodes.DUP, Opcodes.CHECKCAST, Opcodes.INSTANCEOF
|
|
||||||
);
|
|
||||||
|
|
||||||
private static final ImmutableSet<Integer> PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER = ImmutableSet.of(
|
val candidatesBoxedValues = RedundantBoxedValuesCollection()
|
||||||
Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE
|
|
||||||
);
|
|
||||||
|
|
||||||
private final RedundantBoxedValuesCollection values = new RedundantBoxedValuesCollection();
|
@Throws(AnalyzerException::class)
|
||||||
|
override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue): BasicValue? {
|
||||||
|
processOperationWithBoxedValue(value1, insn)
|
||||||
|
processOperationWithBoxedValue(value2, insn)
|
||||||
|
|
||||||
public RedundantBoxingInterpreter(InsnList insnList) {
|
return super.binaryOperation(insn, value1, value2)
|
||||||
super(insnList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Throws(AnalyzerException::class)
|
||||||
public BasicValue binaryOperation(
|
override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue, value3: BasicValue): BasicValue? {
|
||||||
@NotNull AbstractInsnNode insn,
|
|
||||||
@NotNull BasicValue value1,
|
|
||||||
@NotNull BasicValue value2
|
|
||||||
) throws AnalyzerException {
|
|
||||||
|
|
||||||
processOperationWithBoxedValue(value1, insn);
|
|
||||||
processOperationWithBoxedValue(value2, insn);
|
|
||||||
|
|
||||||
return super.binaryOperation(insn, value1, value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BasicValue ternaryOperation(
|
|
||||||
@NotNull AbstractInsnNode insn,
|
|
||||||
@NotNull BasicValue value1, @NotNull BasicValue value2, @NotNull BasicValue value3
|
|
||||||
) throws AnalyzerException {
|
|
||||||
|
|
||||||
// in a valid code only aastore could happen with boxed value
|
// in a valid code only aastore could happen with boxed value
|
||||||
processOperationWithBoxedValue(value3, insn);
|
processOperationWithBoxedValue(value3, insn)
|
||||||
|
|
||||||
return super.ternaryOperation(insn, value1, value2, value3);
|
return super.ternaryOperation(insn, value1, value2, value3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Throws(AnalyzerException::class)
|
||||||
@Override
|
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
|
||||||
public BasicValue unaryOperation(
|
if ((insn.opcode == Opcodes.CHECKCAST || insn.opcode == Opcodes.INSTANCEOF) && value is BoxedBasicValue) {
|
||||||
@NotNull AbstractInsnNode insn, @NotNull BasicValue value
|
val typeInsn = insn as TypeInsnNode
|
||||||
) throws AnalyzerException {
|
|
||||||
|
|
||||||
if ((insn.getOpcode() == Opcodes.CHECKCAST || insn.getOpcode() == Opcodes.INSTANCEOF) &&
|
if (!isSafeCast(value, typeInsn.desc)) {
|
||||||
value instanceof BoxedBasicValue) {
|
markValueAsDirty(value)
|
||||||
TypeInsnNode typeInsn = (TypeInsnNode) insn;
|
|
||||||
|
|
||||||
if (!isSafeCast((BoxedBasicValue) value, typeInsn.desc)) {
|
|
||||||
markValueAsDirty((BoxedBasicValue) value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processOperationWithBoxedValue(value, insn);
|
processOperationWithBoxedValue(value, insn)
|
||||||
|
|
||||||
return super.unaryOperation(insn, value);
|
return super.unaryOperation(insn, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isSafeCast(@NotNull BoxedBasicValue value, @NotNull String targetInternalName) {
|
@Throws(AnalyzerException::class)
|
||||||
if (targetInternalName.equals(Type.getInternalName(Object.class))) return true;
|
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue {
|
||||||
|
if (value is BoxedBasicValue && insn.opcode === Opcodes.ASTORE) {
|
||||||
if (targetInternalName.equals(Type.getInternalName(Number.class))) {
|
value.addVariableIndex((insn as VarInsnNode).`var`)
|
||||||
return PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(
|
|
||||||
value.getPrimitiveType().getSort()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.getType().getInternalName().equals(targetInternalName);
|
processOperationWithBoxedValue(value, insn)
|
||||||
|
|
||||||
|
return super.copyOperation(insn, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
fun processPopInstruction(insnNode: AbstractInsnNode, value: BasicValue) {
|
||||||
@NotNull
|
processOperationWithBoxedValue(value, insnNode)
|
||||||
public BasicValue copyOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
|
||||||
if (value instanceof BoxedBasicValue && insn.getOpcode() == Opcodes.ASTORE) {
|
|
||||||
((BoxedBasicValue) value).addVariableIndex(((VarInsnNode) insn).var);
|
|
||||||
}
|
|
||||||
|
|
||||||
processOperationWithBoxedValue(value, insn);
|
|
||||||
|
|
||||||
return super.copyOperation(insn, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processPopInstruction(@NotNull AbstractInsnNode insnNode, @NotNull BasicValue value) {
|
override fun onNewBoxedValue(value: BoxedBasicValue) {
|
||||||
processOperationWithBoxedValue(value, insnNode);
|
candidatesBoxedValues.add(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {
|
||||||
protected void onNewBoxedValue(@NotNull BoxedBasicValue value) {
|
if (value.primitiveType == resultType) {
|
||||||
values.add(value);
|
addAssociatedInsn(value, insn)
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onUnboxing(
|
|
||||||
@NotNull AbstractInsnNode insn, @NotNull BoxedBasicValue value, @NotNull Type resultType
|
|
||||||
) {
|
|
||||||
if (value.getPrimitiveType().equals(resultType)) {
|
|
||||||
addAssociatedInsn(value, insn);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
value.addUnboxingWithCastTo(insn, resultType);
|
value.addUnboxingWithCastTo(insn, resultType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun onMethodCallWithBoxedValue(value: BoxedBasicValue) {
|
||||||
protected void onMethodCallWithBoxedValue(@NotNull BoxedBasicValue value) {
|
markValueAsDirty(value)
|
||||||
markValueAsDirty(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun onMergeFail(value: BoxedBasicValue) {
|
||||||
protected void onMergeFail(@NotNull BoxedBasicValue v) {
|
markValueAsDirty(value)
|
||||||
markValueAsDirty(v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun onMergeSuccess(v: BoxedBasicValue, w: BoxedBasicValue) {
|
||||||
protected void onMergeSuccess(
|
candidatesBoxedValues.merge(v, w)
|
||||||
@NotNull BoxedBasicValue v, @NotNull BoxedBasicValue w
|
|
||||||
) {
|
|
||||||
values.merge(v, w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processOperationWithBoxedValue(@Nullable BasicValue value, @NotNull AbstractInsnNode insnNode) {
|
private fun processOperationWithBoxedValue(value: BasicValue?, insnNode: AbstractInsnNode) {
|
||||||
if (value instanceof BoxedBasicValue) {
|
if (value is BoxedBasicValue) {
|
||||||
if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.getOpcode())) {
|
if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.opcode)) {
|
||||||
markValueAsDirty((BoxedBasicValue) value);
|
markValueAsDirty(value)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
addAssociatedInsn((BoxedBasicValue) value, insnNode);
|
addAssociatedInsn(value, insnNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void markValueAsDirty(@NotNull BoxedBasicValue value) {
|
private fun markValueAsDirty(value: BoxedBasicValue) {
|
||||||
values.remove(value);
|
candidatesBoxedValues.remove(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addAssociatedInsn(@NotNull BoxedBasicValue value, @NotNull AbstractInsnNode insn) {
|
companion object {
|
||||||
if (value.isSafeToRemove()) {
|
private val PERMITTED_OPERATIONS_OPCODES =
|
||||||
value.addInsn(insn);
|
ImmutableSet.of(Opcodes.ASTORE, Opcodes.ALOAD, Opcodes.POP, Opcodes.DUP, Opcodes.CHECKCAST, Opcodes.INSTANCEOF)
|
||||||
|
|
||||||
|
private val PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER =
|
||||||
|
ImmutableSet.of(Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE)
|
||||||
|
|
||||||
|
private fun isSafeCast(value: BoxedBasicValue, targetInternalName: String) =
|
||||||
|
when (targetInternalName) {
|
||||||
|
Type.getInternalName(Any::class.java) ->
|
||||||
|
true
|
||||||
|
Type.getInternalName(Number::class.java) -> {
|
||||||
|
PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(
|
||||||
|
value.primitiveType.sort)
|
||||||
|
}
|
||||||
|
else ->
|
||||||
|
value.type.internalName.equals(targetInternalName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addAssociatedInsn(value: BoxedBasicValue, insn: AbstractInsnNode) {
|
||||||
|
if (value.isSafeToRemove) {
|
||||||
|
value.addInsn(insn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@NotNull
|
|
||||||
public RedundantBoxedValuesCollection getCandidatesBoxedValues() {
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user