Fixes after review
This commit is contained in:
committed by
Alexander Udalov
parent
d604b5de79
commit
b1a54a424e
@@ -115,6 +115,16 @@ public class RangeCodegenUtil {
|
|||||||
return map.get(DescriptorUtils.getFqNameSafe(declarationDescriptor));
|
return map.get(DescriptorUtils.getFqNameSafe(declarationDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static PrimitiveType getPrimitiveRangeOrProgressionElementType(@NotNull FqName rangeOrProgressionName) {
|
||||||
|
PrimitiveType result = RANGE_TO_ELEMENT_TYPE.get(rangeOrProgressionName);
|
||||||
|
return result != null ? result : PROGRESSION_TO_ELEMENT_TYPE.get(rangeOrProgressionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRangeOrProgression(@NotNull FqName className) {
|
||||||
|
return getPrimitiveRangeOrProgressionElementType(className) != null;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isOptimizableRangeTo(CallableDescriptor rangeTo) {
|
public static boolean isOptimizableRangeTo(CallableDescriptor rangeTo) {
|
||||||
if ("rangeTo".equals(rangeTo.getName().asString())) {
|
if ("rangeTo".equals(rangeTo.getName().asString())) {
|
||||||
if (isPrimitiveNumberClassDescriptor(rangeTo.getContainingDeclaration())) {
|
if (isPrimitiveNumberClassDescriptor(rangeTo.getContainingDeclaration())) {
|
||||||
|
|||||||
+5
-6
@@ -30,7 +30,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class OptimizationMethodVisitor extends MethodVisitor {
|
public class OptimizationMethodVisitor extends MethodVisitor {
|
||||||
private final MethodTransformer methodTransformer = new RedundantNullCheckMethodTransformer(
|
private static final MethodTransformer MAIN_METHOD_TRANSFORMER = new RedundantNullCheckMethodTransformer(
|
||||||
new RedundantBoxingMethodTransformer(null)
|
new RedundantBoxingMethodTransformer(null)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -62,15 +62,14 @@ public class OptimizationMethodVisitor extends MethodVisitor {
|
|||||||
super.visitEnd();
|
super.visitEnd();
|
||||||
|
|
||||||
if (methodNode.instructions.size() > 0) {
|
if (methodNode.instructions.size() > 0) {
|
||||||
methodTransformer.transform("fake", methodNode);
|
MAIN_METHOD_TRANSFORMER.transform("fake", methodNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
methodNode.accept(new EndIgnoringMethodVisitorDecorator(OptimizationUtils.API, delegate));
|
methodNode.accept(new EndIgnoringMethodVisitorDecorator(OptimizationUtils.API, delegate));
|
||||||
|
|
||||||
/*
|
|
||||||
In case of empty instructions list MethodNode.accept doesn't call visitLocalVariables of delegate
|
// In case of empty instructions list MethodNode.accept doesn't call visitLocalVariables of delegate
|
||||||
So we just do it here
|
// So we just do it here
|
||||||
*/
|
|
||||||
if (methodNode.instructions.size() == 0) {
|
if (methodNode.instructions.size() == 0) {
|
||||||
List<LocalVariableNode> localVariables = methodNode.localVariables;
|
List<LocalVariableNode> localVariables = methodNode.localVariables;
|
||||||
// visits local variables
|
// visits local variables
|
||||||
|
|||||||
+11
-7
@@ -34,14 +34,18 @@ public class BoxedBasicValue extends BasicValue {
|
|||||||
private final Set<Integer> associatedVariables = new HashSet<Integer>();
|
private final Set<Integer> associatedVariables = new HashSet<Integer>();
|
||||||
private final Set<BoxedBasicValue> mergedWith = new HashSet<BoxedBasicValue>();
|
private final Set<BoxedBasicValue> mergedWith = new HashSet<BoxedBasicValue>();
|
||||||
private final Type primitiveType;
|
private final Type primitiveType;
|
||||||
private final RangeIteratorBasicValue numberIterator;
|
private final ProgressionIteratorBasicValue progressionIterator;
|
||||||
private boolean isSafeToRemove = true;
|
private boolean isSafeToRemove = true;
|
||||||
|
|
||||||
public BoxedBasicValue(Type boxedType, AbstractInsnNode boxingInsn, @Nullable RangeIteratorBasicValue numberIterator) {
|
public BoxedBasicValue(
|
||||||
|
@NotNull Type boxedType,
|
||||||
|
@NotNull AbstractInsnNode boxingInsn,
|
||||||
|
@Nullable ProgressionIteratorBasicValue progressionIterator
|
||||||
|
) {
|
||||||
super(boxedType);
|
super(boxedType);
|
||||||
this.primitiveType = AsmUtil.unboxType(boxedType);
|
this.primitiveType = AsmUtil.unboxType(boxedType);
|
||||||
this.boxingInsn = boxingInsn;
|
this.boxingInsn = boxingInsn;
|
||||||
this.numberIterator = numberIterator;
|
this.progressionIterator = progressionIterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,12 +126,12 @@ public class BoxedBasicValue extends BasicValue {
|
|||||||
return boxingInsn;
|
return boxingInsn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFromNumberIterator() {
|
public boolean isFromProgressionIterator() {
|
||||||
return numberIterator != null;
|
return progressionIterator != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public RangeIteratorBasicValue getNumberIterator() {
|
public ProgressionIteratorBasicValue getProgressionIterator() {
|
||||||
return numberIterator;
|
return progressionIterator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-45
@@ -20,8 +20,11 @@ import com.google.common.collect.ImmutableSet;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.codegen.AsmUtil;
|
import org.jetbrains.jet.codegen.AsmUtil;
|
||||||
|
import org.jetbrains.jet.codegen.RangeCodegenUtil;
|
||||||
import org.jetbrains.jet.codegen.optimization.common.OptimizationBasicInterpreter;
|
import org.jetbrains.jet.codegen.optimization.common.OptimizationBasicInterpreter;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
|
import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
|
||||||
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
|
import org.jetbrains.jet.lang.types.lang.PrimitiveType;
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
@@ -35,54 +38,58 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
||||||
private static boolean isProgressionClass(String internalName) {
|
private static final ImmutableSet<String> UNBOXING_METHOD_NAMES;
|
||||||
return internalName.startsWith("kotlin/") && (
|
|
||||||
internalName.endsWith("Progression") ||
|
static {
|
||||||
internalName.endsWith("Range")
|
UNBOXING_METHOD_NAMES = ImmutableSet.of(
|
||||||
|
"booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isProgressionClass(String internalClassName) {
|
||||||
|
return RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* e.g. for "kotlin/IntRange" it returns "Int"
|
* e.g. for "kotlin/IntRange" it returns "Int"
|
||||||
*
|
*
|
||||||
* @param progressionClassInternalName
|
* @param progressionClassInternalName
|
||||||
* @return
|
* @return
|
||||||
|
* @throws java.lang.AssertionError if progressionClassInternalName is not progression class internal name
|
||||||
*/
|
*/
|
||||||
private static String getValuesTypePartOfProgressionClass(String progressionClassInternalName) {
|
@NotNull
|
||||||
progressionClassInternalName = progressionClassInternalName.substring("kotlin/".length());
|
private static String getValuesTypeOfProgressionClass(String progressionClassInternalName) {
|
||||||
|
PrimitiveType type = RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(
|
||||||
|
buildFqNameByInternal(progressionClassInternalName)
|
||||||
|
);
|
||||||
|
|
||||||
int cutAtTheEnd = (progressionClassInternalName.endsWith("Progression")) ? "Progression".length() : "Range".length();
|
assert type != null : "type should be not null";
|
||||||
return progressionClassInternalName.substring(0, progressionClassInternalName.length() - cutAtTheEnd);
|
|
||||||
|
return type.getTypeName().asString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final ImmutableSet<String> wrappersClassNames;
|
private static boolean isWrapperClassName(@NotNull String internalClassName) {
|
||||||
|
return JvmPrimitiveType.isWrapperClassName(
|
||||||
static {
|
buildFqNameByInternal(internalClassName)
|
||||||
ImmutableSet.Builder<String> wrappersClassesBuilder = ImmutableSet.builder();
|
);
|
||||||
|
|
||||||
for (JvmPrimitiveType primitiveType : JvmPrimitiveType.values()) {
|
|
||||||
wrappersClassesBuilder.add(AsmUtil.internalNameByFqNameWithoutInnerClasses(primitiveType.getWrapperFqName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
wrappersClassNames = wrappersClassesBuilder.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isWrapperClassName(@NotNull String owner) {
|
@NotNull
|
||||||
return wrappersClassNames.contains(owner);
|
private static FqName buildFqNameByInternal(@NotNull String internalClassName) {
|
||||||
|
return new FqName(Type.getObjectType(internalClassName).getClassName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isWrapperClassNameOrNumber(@NotNull String owner) {
|
private static boolean isWrapperClassNameOrNumber(@NotNull String internalClassName) {
|
||||||
return isWrapperClassName(owner) || owner.equals(Type.getInternalName(Number.class));
|
return isWrapperClassName(internalClassName) || internalClassName.equals(Type.getInternalName(Number.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isUnboxingMethodName(@NotNull String name) {
|
private static boolean isUnboxingMethodName(@NotNull String name) {
|
||||||
return name.endsWith("Value");
|
return UNBOXING_METHOD_NAMES.contains(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isUnboxing(@NotNull AbstractInsnNode insn) {
|
private static boolean isUnboxing(@NotNull AbstractInsnNode insn) {
|
||||||
if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) {
|
if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodInsnNode methodInsn = (MethodInsnNode) insn;
|
MethodInsnNode methodInsn = (MethodInsnNode) insn;
|
||||||
|
|
||||||
@@ -90,13 +97,11 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isBoxing(@NotNull AbstractInsnNode insn) {
|
private static boolean isBoxing(@NotNull AbstractInsnNode insn) {
|
||||||
if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
|
if (insn.getOpcode() != Opcodes.INVOKESTATIC) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
|
MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
|
||||||
|
|
||||||
return isWrapperClassName(methodInsnNode.owner) && methodInsnNode.name.equals("valueOf");
|
return isWrapperClassName(methodInsnNode.owner) && "valueOf".equals(methodInsnNode.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isIteratorMethodCallOfProgression(
|
private static boolean isIteratorMethodCallOfProgression(
|
||||||
@@ -105,31 +110,32 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
return (insn.getOpcode() == Opcodes.INVOKEINTERFACE &&
|
return (insn.getOpcode() == Opcodes.INVOKEINTERFACE &&
|
||||||
values.get(0).getType() != null &&
|
values.get(0).getType() != null &&
|
||||||
isProgressionClass(values.get(0).getType().getInternalName()) &&
|
isProgressionClass(values.get(0).getType().getInternalName()) &&
|
||||||
((MethodInsnNode) insn).name.equals("iterator"));
|
"iterator".equals(((MethodInsnNode) insn).name));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isNextMethodCallOfProgressionIterator(
|
private static boolean isNextMethodCallOfProgressionIterator(
|
||||||
@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values
|
@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values
|
||||||
) {
|
) {
|
||||||
return (insn.getOpcode() == Opcodes.INVOKEINTERFACE &&
|
return (insn.getOpcode() == Opcodes.INVOKEINTERFACE &&
|
||||||
values.get(0) instanceof RangeIteratorBasicValue &&
|
values.get(0) instanceof ProgressionIteratorBasicValue &&
|
||||||
((MethodInsnNode) insn).name.equals("next"));
|
"next".equals(((MethodInsnNode) insn).name));
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<Integer, BoxedBasicValue> boxingPlaces = new HashMap<Integer, BoxedBasicValue>();
|
private final Map<Integer, BoxedBasicValue> boxingPlaces = new HashMap<Integer, BoxedBasicValue>();
|
||||||
private final InsnList insnList;
|
private final InsnList insnList;
|
||||||
|
|
||||||
BoxingInterpreter(InsnList insnList) {
|
public BoxingInterpreter(InsnList insnList) {
|
||||||
this.insnList = insnList;
|
this.insnList = insnList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private BoxedBasicValue createNewBoxing(
|
private BoxedBasicValue createNewBoxing(
|
||||||
@NotNull AbstractInsnNode insn, @NotNull Type type, @Nullable RangeIteratorBasicValue numberIterator
|
@NotNull AbstractInsnNode insn, @NotNull Type type,
|
||||||
|
@Nullable ProgressionIteratorBasicValue progressionIterator
|
||||||
) {
|
) {
|
||||||
int index = insnList.indexOf(insn);
|
int index = insnList.indexOf(insn);
|
||||||
if (!boxingPlaces.containsKey(index)) {
|
if (!boxingPlaces.containsKey(index)) {
|
||||||
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(type, insn, numberIterator);
|
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(type, insn, progressionIterator);
|
||||||
onNewBoxedValue(boxedBasicValue);
|
onNewBoxedValue(boxedBasicValue);
|
||||||
boxingPlaces.put(index, boxedBasicValue);
|
boxingPlaces.put(index, boxedBasicValue);
|
||||||
}
|
}
|
||||||
@@ -142,28 +148,37 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
public BasicValue naryOperation(@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values) throws AnalyzerException {
|
public BasicValue naryOperation(@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values) throws AnalyzerException {
|
||||||
BasicValue value = super.naryOperation(insn, values);
|
BasicValue value = super.naryOperation(insn, values);
|
||||||
|
|
||||||
|
if (values.isEmpty()) return value;
|
||||||
|
|
||||||
|
BasicValue firstArg = values.get(0);
|
||||||
|
|
||||||
if (isBoxing(insn)) {
|
if (isBoxing(insn)) {
|
||||||
return createNewBoxing(insn, value.getType(), null);
|
return createNewBoxing(insn, value.getType(), null);
|
||||||
}
|
}
|
||||||
else if (isUnboxing(insn) &&
|
else if (isUnboxing(insn) &&
|
||||||
values.get(0) instanceof BoxedBasicValue &&
|
firstArg instanceof BoxedBasicValue &&
|
||||||
value.getType().equals(((BoxedBasicValue) values.get(0)).getPrimitiveType())) {
|
value.getType().equals(((BoxedBasicValue) firstArg).getPrimitiveType())) {
|
||||||
onUnboxing((BoxedBasicValue) values.get(0), insn);
|
onUnboxing((BoxedBasicValue) firstArg, insn);
|
||||||
}
|
}
|
||||||
else if (isIteratorMethodCallOfProgression(insn, values)) {
|
else if (isIteratorMethodCallOfProgression(insn, values)) {
|
||||||
return new RangeIteratorBasicValue(
|
return new ProgressionIteratorBasicValue(
|
||||||
getValuesTypePartOfProgressionClass(values.get(0).getType().getInternalName())
|
getValuesTypeOfProgressionClass(firstArg.getType().getInternalName())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (isNextMethodCallOfProgressionIterator(insn, values)) {
|
else if (isNextMethodCallOfProgressionIterator(insn, values)) {
|
||||||
RangeIteratorBasicValue numberIterator = (RangeIteratorBasicValue) values.get(0);
|
assert firstArg instanceof ProgressionIteratorBasicValue : "firstArg should be progression iterator";
|
||||||
|
|
||||||
|
ProgressionIteratorBasicValue progressionIterator = (ProgressionIteratorBasicValue) firstArg;
|
||||||
return createNewBoxing(
|
return createNewBoxing(
|
||||||
insn,
|
insn,
|
||||||
AsmUtil.boxType(numberIterator.getValuesPrimitiveType()),
|
AsmUtil.boxType(progressionIterator.getValuesPrimitiveType()),
|
||||||
numberIterator
|
progressionIterator
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
for (BasicValue arg : values) {
|
||||||
if (arg instanceof BoxedBasicValue) {
|
if (arg instanceof BoxedBasicValue) {
|
||||||
onMethodCallWithBoxedValue((BoxedBasicValue) arg);
|
onMethodCallWithBoxedValue((BoxedBasicValue) arg);
|
||||||
@@ -175,7 +190,7 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isExactValue(@NotNull BasicValue value) {
|
private static boolean isExactValue(@NotNull BasicValue value) {
|
||||||
return value instanceof RangeIteratorBasicValue ||
|
return value instanceof ProgressionIteratorBasicValue ||
|
||||||
value instanceof BoxedBasicValue ||
|
value instanceof BoxedBasicValue ||
|
||||||
(value.getType() != null && isProgressionClass(value.getType().getInternalName()));
|
(value.getType() != null && isProgressionClass(value.getType().getInternalName()));
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-26
@@ -16,41 +16,37 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.codegen.optimization.boxing;
|
package org.jetbrains.jet.codegen.optimization.boxing;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||||
|
|
||||||
public class RangeIteratorBasicValue extends BasicValue {
|
public class ProgressionIteratorBasicValue extends BasicValue {
|
||||||
private static Type getValuesType(@NotNull String valuesTypeName) {
|
private final static ImmutableMap<String, Type> VALUES_TYPENAME_TO_TYPE;
|
||||||
if (valuesTypeName.equals("Byte")) {
|
|
||||||
return Type.BYTE_TYPE;
|
|
||||||
}
|
|
||||||
if (valuesTypeName.equals("Char")) {
|
|
||||||
return Type.CHAR_TYPE;
|
|
||||||
}
|
|
||||||
if (valuesTypeName.equals("Short")) {
|
|
||||||
return Type.SHORT_TYPE;
|
|
||||||
}
|
|
||||||
if (valuesTypeName.equals("Int")) {
|
|
||||||
return Type.INT_TYPE;
|
|
||||||
}
|
|
||||||
if (valuesTypeName.equals("Long")) {
|
|
||||||
return Type.LONG_TYPE;
|
|
||||||
}
|
|
||||||
if (valuesTypeName.equals("Float")) {
|
|
||||||
return Type.FLOAT_TYPE;
|
|
||||||
}
|
|
||||||
if (valuesTypeName.equals("Double")) {
|
|
||||||
return Type.DOUBLE_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("Unexpected type " + valuesTypeName);
|
static {
|
||||||
|
VALUES_TYPENAME_TO_TYPE = ImmutableMap.<String, Type>builder().
|
||||||
|
put("Byte", Type.BYTE_TYPE).
|
||||||
|
put("Char", Type.CHAR_TYPE).
|
||||||
|
put("Short", Type.SHORT_TYPE).
|
||||||
|
put("Int", Type.INT_TYPE).
|
||||||
|
put("Long", Type.LONG_TYPE).
|
||||||
|
put("Float", Type.FLOAT_TYPE).
|
||||||
|
put("Double", Type.DOUBLE_TYPE).
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static Type getValuesType(@NotNull String valuesTypeName) {
|
||||||
|
Type type = VALUES_TYPENAME_TO_TYPE.get(valuesTypeName);
|
||||||
|
assert type != null : "Unexpected type " + valuesTypeName;
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Type valuesPrimitiveType;
|
private final Type valuesPrimitiveType;
|
||||||
private final String valuesPrimitiveTypeName;
|
private final String valuesPrimitiveTypeName;
|
||||||
|
|
||||||
public RangeIteratorBasicValue(@NotNull String valuesPrimitiveTypeName) {
|
public ProgressionIteratorBasicValue(@NotNull String valuesPrimitiveTypeName) {
|
||||||
super(Type.getObjectType("kotlin/" + valuesPrimitiveTypeName + "Iterator"));
|
super(Type.getObjectType("kotlin/" + valuesPrimitiveTypeName + "Iterator"));
|
||||||
this.valuesPrimitiveType = getValuesType(valuesPrimitiveTypeName);
|
this.valuesPrimitiveType = getValuesType(valuesPrimitiveTypeName);
|
||||||
this.valuesPrimitiveTypeName = valuesPrimitiveTypeName;
|
this.valuesPrimitiveTypeName = valuesPrimitiveTypeName;
|
||||||
@@ -70,7 +66,7 @@ public class RangeIteratorBasicValue extends BasicValue {
|
|||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
if (!super.equals(o)) return false;
|
if (!super.equals(o)) return false;
|
||||||
|
|
||||||
RangeIteratorBasicValue value = (RangeIteratorBasicValue) o;
|
ProgressionIteratorBasicValue value = (ProgressionIteratorBasicValue) o;
|
||||||
|
|
||||||
if (!valuesPrimitiveType.equals(value.valuesPrimitiveType)) return false;
|
if (!valuesPrimitiveType.equals(value.valuesPrimitiveType)) return false;
|
||||||
|
|
||||||
+6
-4
@@ -30,7 +30,7 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
class RedundantBoxingInterpreter extends BoxingInterpreter {
|
class RedundantBoxingInterpreter extends BoxingInterpreter {
|
||||||
private static final ImmutableSet<Integer> unsafeOperationsOpcodes;
|
private static final ImmutableSet<Integer> UNSAFE_OPERATIONS_OPCODES;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ImmutableSet.Builder<Integer> unsafeOperationsOpcodesBuilder = ImmutableSet.builder();
|
ImmutableSet.Builder<Integer> unsafeOperationsOpcodesBuilder = ImmutableSet.builder();
|
||||||
@@ -39,12 +39,12 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
|||||||
Opcodes.IFNONNULL, Opcodes.IFNULL, Opcodes.IF_ACMPEQ, Opcodes.IF_ACMPNE
|
Opcodes.IFNONNULL, Opcodes.IFNULL, Opcodes.IF_ACMPEQ, Opcodes.IF_ACMPNE
|
||||||
);
|
);
|
||||||
|
|
||||||
unsafeOperationsOpcodes = unsafeOperationsOpcodesBuilder.build();
|
UNSAFE_OPERATIONS_OPCODES = unsafeOperationsOpcodesBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Set<BoxedBasicValue> candidatesBoxedValues = new HashSet<BoxedBasicValue>();
|
private final Set<BoxedBasicValue> candidatesBoxedValues = new HashSet<BoxedBasicValue>();
|
||||||
|
|
||||||
RedundantBoxingInterpreter(InsnList insnList) {
|
public RedundantBoxingInterpreter(InsnList insnList) {
|
||||||
super(insnList);
|
super(insnList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
|||||||
|
|
||||||
private static void processOperationWithBoxedValue(@Nullable BasicValue value, @NotNull AbstractInsnNode insnNode) {
|
private static void processOperationWithBoxedValue(@Nullable BasicValue value, @NotNull AbstractInsnNode insnNode) {
|
||||||
if (value instanceof BoxedBasicValue) {
|
if (value instanceof BoxedBasicValue) {
|
||||||
if (unsafeOperationsOpcodes.contains(insnNode.getOpcode())) {
|
if (UNSAFE_OPERATIONS_OPCODES.contains(insnNode.getOpcode())) {
|
||||||
markValueAsDirty((BoxedBasicValue) value);
|
markValueAsDirty((BoxedBasicValue) value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -76,6 +76,7 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
|||||||
@NotNull BasicValue value2
|
@NotNull BasicValue value2
|
||||||
) throws AnalyzerException {
|
) throws AnalyzerException {
|
||||||
|
|
||||||
|
processOperationWithBoxedValue(value1, insn);
|
||||||
processOperationWithBoxedValue(value2, insn);
|
processOperationWithBoxedValue(value2, insn);
|
||||||
|
|
||||||
return super.binaryOperation(insn, value1, value2);
|
return super.binaryOperation(insn, value1, value2);
|
||||||
@@ -87,6 +88,7 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
|||||||
@NotNull BasicValue value1, @NotNull BasicValue value2, @NotNull BasicValue value3
|
@NotNull BasicValue value1, @NotNull BasicValue value2, @NotNull BasicValue value3
|
||||||
) throws AnalyzerException {
|
) throws AnalyzerException {
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|||||||
+19
-20
@@ -36,27 +36,26 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transform(@NotNull String owner, @NotNull MethodNode node) {
|
public void transform(@NotNull String internalClassName, @NotNull MethodNode node) {
|
||||||
RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions);
|
RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions);
|
||||||
Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(interpreter);
|
Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(interpreter);
|
||||||
|
|
||||||
Frame<BasicValue>[] frames = runAnalyzer(analyzer, owner, node);
|
Frame<BasicValue>[] frames = runAnalyzer(analyzer, internalClassName, node);
|
||||||
Set<BoxedBasicValue> valuesToOptimize = filterSafeToRemoveValues(interpreter.getCandidatesBoxedValues());
|
Set<BoxedBasicValue> valuesToOptimize = filterSafeToRemoveValues(interpreter.getCandidatesBoxedValues());
|
||||||
|
|
||||||
if (valuesToOptimize.size() > 0) {
|
if (!valuesToOptimize.isEmpty()) {
|
||||||
findValuesClashingWithVariables(node, frames);
|
findValuesClashingWithVariables(node, frames);
|
||||||
|
|
||||||
valuesToOptimize = filterSafeToRemoveValues(valuesToOptimize);
|
valuesToOptimize = filterSafeToRemoveValues(valuesToOptimize);
|
||||||
|
|
||||||
adaptLocalVariableTableForBoxedValues(node, frames);
|
adaptLocalVariableTableForBoxedValues(node, frames);
|
||||||
|
|
||||||
int[] remapping = buildVariablesRemapping(valuesToOptimize, node);
|
applyVariablesRemapping(node, buildVariablesRemapping(valuesToOptimize, node));
|
||||||
applyVariablesRemapping(node, remapping);
|
|
||||||
|
|
||||||
adaptInstructionsForBoxedValues(node, frames, valuesToOptimize);
|
adaptInstructionsForBoxedValues(node, frames, valuesToOptimize);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.transform(owner, node);
|
super.transform(internalClassName, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -108,12 +107,12 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
private static void findValuesClashingWithVariables(
|
private static void findValuesClashingWithVariables(
|
||||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
||||||
) {
|
) {
|
||||||
|
while (findValuesClashingWithVariablesPass(node, frames)) {
|
||||||
while (findValuesClashingWithVariablesIteration(node, frames)) {
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean findValuesClashingWithVariablesIteration(
|
private static boolean findValuesClashingWithVariablesPass(
|
||||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
||||||
) {
|
) {
|
||||||
InsnList insnList = node.instructions;
|
InsnList insnList = node.instructions;
|
||||||
@@ -154,7 +153,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
Type usedAsType = null;
|
Type usedAsType = null;
|
||||||
|
|
||||||
for (int i = from; i <= to; i++) {
|
for (int i = from; i <= to; i++) {
|
||||||
AbstractInsnNode insn = insnList.get(i);
|
AbstractInsnNode insn = insnList.get(i); //TODO: handle exception?
|
||||||
if (insn.getOpcode() == Opcodes.ASTORE && ((VarInsnNode) insn).var == varIndex) {
|
if (insn.getOpcode() == Opcodes.ASTORE && ((VarInsnNode) insn).var == varIndex) {
|
||||||
if (frames[i] == null) {
|
if (frames[i] == null) {
|
||||||
return true;
|
return true;
|
||||||
@@ -207,21 +206,21 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static int[] buildVariablesRemapping(@NotNull Iterable<BoxedBasicValue> values, @NotNull MethodNode node) {
|
private static int[] buildVariablesRemapping(@NotNull Iterable<BoxedBasicValue> values, @NotNull MethodNode node) {
|
||||||
Set<Integer> longTypesVars = new HashSet<Integer>();
|
Set<Integer> doubleSizedVars = new HashSet<Integer>();
|
||||||
for (BoxedBasicValue value : values) {
|
for (BoxedBasicValue value : values) {
|
||||||
if (value.getPrimitiveType().getSize() == 2) {
|
if (value.getPrimitiveType().getSize() == 2) {
|
||||||
longTypesVars.addAll(value.getVariablesIndexes());
|
doubleSizedVars.addAll(value.getVariablesIndexes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node.maxLocals += longTypesVars.size();
|
node.maxLocals += doubleSizedVars.size();
|
||||||
int[] remapping = new int[node.maxLocals];
|
int[] remapping = new int[node.maxLocals];
|
||||||
for (int i = 0; i < remapping.length; i++) {
|
for (int i = 0; i < remapping.length; i++) {
|
||||||
remapping[i] = i;
|
remapping[i] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int longVarIndex : longTypesVars) {
|
for (int varIndex : doubleSizedVars) {
|
||||||
for (int i = longVarIndex + 1; i < remapping.length; i++) {
|
for (int i = varIndex + 1; i < remapping.length; i++) {
|
||||||
remapping[i]++;
|
remapping[i]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,14 +246,14 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
private static void adaptInstructionsForBoxedValues(
|
private static void adaptInstructionsForBoxedValues(
|
||||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames, @NotNull Set<BoxedBasicValue> values
|
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames, @NotNull Set<BoxedBasicValue> values
|
||||||
) {
|
) {
|
||||||
collectPopInstructionsForBoxedValues(node, frames);
|
addPopInstructionsForBoxedValues(node, frames);
|
||||||
|
|
||||||
for (BoxedBasicValue value : values) {
|
for (BoxedBasicValue value : values) {
|
||||||
adaptInstructionsForBoxedValue(node, value);
|
adaptInstructionsForBoxedValue(node, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void collectPopInstructionsForBoxedValues(
|
private static void addPopInstructionsForBoxedValues(
|
||||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
||||||
) {
|
) {
|
||||||
for (int i = 0; i < node.instructions.size(); i++) {
|
for (int i = 0; i < node.instructions.size(); i++) {
|
||||||
@@ -279,12 +278,12 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void adaptBoxingInstruction(@NotNull MethodNode node, @NotNull BoxedBasicValue value) {
|
private static void adaptBoxingInstruction(@NotNull MethodNode node, @NotNull BoxedBasicValue value) {
|
||||||
if (!value.isFromNumberIterator()) {
|
if (!value.isFromProgressionIterator()) {
|
||||||
node.instructions.remove(value.getBoxingInsn());
|
node.instructions.remove(value.getBoxingInsn());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
RangeIteratorBasicValue iterator = value.getNumberIterator();
|
ProgressionIteratorBasicValue iterator = value.getProgressionIterator();
|
||||||
assert iterator != null : "iterator should not be null because isFromNumberIterator returns true";
|
assert iterator != null : "iterator should not be null because isFromProgressionIterator returns true";
|
||||||
|
|
||||||
//add checkcast to kotlin/<T>Iterator before next() call
|
//add checkcast to kotlin/<T>Iterator before next() call
|
||||||
node.instructions.insertBefore(
|
node.instructions.insertBefore(
|
||||||
|
|||||||
+6
-6
@@ -36,22 +36,22 @@ public class RedundantNullCheckMethodTransformer extends MethodTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transform(@NotNull String owner, @NotNull MethodNode methodNode) {
|
public void transform(@NotNull String internalClassName, @NotNull MethodNode methodNode) {
|
||||||
|
|
||||||
while (removeRedundantNullCheck(owner, methodNode)) {
|
|
||||||
|
|
||||||
|
while (removeRedundantNullCheckPass(internalClassName, methodNode)) {
|
||||||
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
super.transform(owner, methodNode);
|
super.transform(internalClassName, methodNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean removeRedundantNullCheck(@NotNull String owner, @NotNull MethodNode methodNode) {
|
private static boolean removeRedundantNullCheckPass(@NotNull String internalClassName, @NotNull MethodNode methodNode) {
|
||||||
InsnList insnList = methodNode.instructions;
|
InsnList insnList = methodNode.instructions;
|
||||||
Frame<BasicValue>[] frames = runAnalyzer(
|
Frame<BasicValue>[] frames = runAnalyzer(
|
||||||
new Analyzer<BasicValue>(
|
new Analyzer<BasicValue>(
|
||||||
new BoxingInterpreter(insnList)
|
new BoxingInterpreter(insnList)
|
||||||
),
|
),
|
||||||
owner, methodNode
|
internalClassName, methodNode
|
||||||
);
|
);
|
||||||
|
|
||||||
List<AbstractInsnNode> insnsToOptimize = new ArrayList<AbstractInsnNode>();
|
List<AbstractInsnNode> insnsToOptimize = new ArrayList<AbstractInsnNode>();
|
||||||
|
|||||||
+4
-4
@@ -32,20 +32,20 @@ public abstract class MethodTransformer {
|
|||||||
|
|
||||||
protected static <V extends Value> Frame<V>[] runAnalyzer(
|
protected static <V extends Value> Frame<V>[] runAnalyzer(
|
||||||
@NotNull Analyzer<V> analyzer,
|
@NotNull Analyzer<V> analyzer,
|
||||||
@NotNull String owner,
|
@NotNull String internalClassName,
|
||||||
@NotNull MethodNode node
|
@NotNull MethodNode node
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return analyzer.analyze(owner, node);
|
return analyzer.analyze(internalClassName, node);
|
||||||
}
|
}
|
||||||
catch (AnalyzerException e) {
|
catch (AnalyzerException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void transform(@NotNull String owner, @NotNull MethodNode methodNode) {
|
public void transform(@NotNull String internalClassName, @NotNull MethodNode methodNode) {
|
||||||
if (delegate != null) {
|
if (delegate != null) {
|
||||||
delegate.transform(owner, methodNode);
|
delegate.transform(internalClassName, methodNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user