Converting optimization.boxing to Kotlin: convert & simplify

This commit is contained in:
Dmitry Petrov
2016-03-03 11:11:35 +03:00
parent 0555464a3b
commit 2ecb8896cc
3 changed files with 260 additions and 424 deletions
@@ -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;
}
} }
@@ -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")
} }
} }
@@ -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;
}
}