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