Do not use BasicInterpreter for bytecode analysis
The problem with BasicInterpreter is the following: it creates BasicValue objects that should be replaced in favor of StrictBasicicValue See commit message in 25c6ac1 for clarification #KT-14447 Fixed
This commit is contained in:
+268
-6
@@ -18,16 +18,24 @@ package org.jetbrains.kotlin.codegen.optimization.common;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.org.objectweb.asm.Handle;
|
||||||
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.*;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter;
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue.*;
|
import static org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue.*;
|
||||||
|
|
||||||
public class OptimizationBasicInterpreter extends BasicInterpreter {
|
public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
|
||||||
|
|
||||||
|
public OptimizationBasicInterpreter() {
|
||||||
|
super(ASM5);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public StrictBasicValue newValue(@Nullable Type type) {
|
public StrictBasicValue newValue(@Nullable Type type) {
|
||||||
@@ -67,7 +75,78 @@ public class OptimizationBasicInterpreter extends BasicInterpreter {
|
|||||||
if (insn.getOpcode() == Opcodes.ACONST_NULL) {
|
if (insn.getOpcode() == Opcodes.ACONST_NULL) {
|
||||||
return newValue(Type.getObjectType("java/lang/Object"));
|
return newValue(Type.getObjectType("java/lang/Object"));
|
||||||
}
|
}
|
||||||
return super.newOperation(insn);
|
|
||||||
|
switch (insn.getOpcode()) {
|
||||||
|
case ACONST_NULL:
|
||||||
|
return newValue(Type.getObjectType("null"));
|
||||||
|
case ICONST_M1:
|
||||||
|
case ICONST_0:
|
||||||
|
case ICONST_1:
|
||||||
|
case ICONST_2:
|
||||||
|
case ICONST_3:
|
||||||
|
case ICONST_4:
|
||||||
|
case ICONST_5:
|
||||||
|
return StrictBasicValue.INT_VALUE;
|
||||||
|
case LCONST_0:
|
||||||
|
case LCONST_1:
|
||||||
|
return StrictBasicValue.LONG_VALUE;
|
||||||
|
case FCONST_0:
|
||||||
|
case FCONST_1:
|
||||||
|
case FCONST_2:
|
||||||
|
return StrictBasicValue.FLOAT_VALUE;
|
||||||
|
case DCONST_0:
|
||||||
|
case DCONST_1:
|
||||||
|
return StrictBasicValue.DOUBLE_VALUE;
|
||||||
|
case BIPUSH:
|
||||||
|
case SIPUSH:
|
||||||
|
return StrictBasicValue.INT_VALUE;
|
||||||
|
case LDC:
|
||||||
|
Object cst = ((LdcInsnNode) insn).cst;
|
||||||
|
if (cst instanceof Integer) {
|
||||||
|
return StrictBasicValue.INT_VALUE;
|
||||||
|
}
|
||||||
|
else if (cst instanceof Float) {
|
||||||
|
return StrictBasicValue.FLOAT_VALUE;
|
||||||
|
}
|
||||||
|
else if (cst instanceof Long) {
|
||||||
|
return StrictBasicValue.LONG_VALUE;
|
||||||
|
}
|
||||||
|
else if (cst instanceof Double) {
|
||||||
|
return StrictBasicValue.DOUBLE_VALUE;
|
||||||
|
}
|
||||||
|
else if (cst instanceof String) {
|
||||||
|
return newValue(Type.getObjectType("java/lang/String"));
|
||||||
|
}
|
||||||
|
else if (cst instanceof Type) {
|
||||||
|
int sort = ((Type) cst).getSort();
|
||||||
|
if (sort == Type.OBJECT || sort == Type.ARRAY) {
|
||||||
|
return newValue(Type.getObjectType("java/lang/Class"));
|
||||||
|
}
|
||||||
|
else if (sort == Type.METHOD) {
|
||||||
|
return newValue(Type.getObjectType("java/lang/invoke/MethodType"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Illegal LDC constant " + cst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (cst instanceof Handle) {
|
||||||
|
return newValue(Type.getObjectType("java/lang/invoke/MethodHandle"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Illegal LDC constant " + cst);
|
||||||
|
}
|
||||||
|
case GETSTATIC:
|
||||||
|
return newValue(Type.getType(((FieldInsnNode) insn).desc));
|
||||||
|
case NEW:
|
||||||
|
return newValue(Type.getObjectType(((TypeInsnNode) insn).desc));
|
||||||
|
default:
|
||||||
|
throw new Error("Internal error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicValue copyOperation(@NotNull AbstractInsnNode insn, BasicValue value) throws AnalyzerException {
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -79,11 +158,194 @@ public class OptimizationBasicInterpreter extends BasicInterpreter {
|
|||||||
if (insn.getOpcode() == Opcodes.AALOAD) {
|
if (insn.getOpcode() == Opcodes.AALOAD) {
|
||||||
Type arrayType = value1.getType();
|
Type arrayType = value1.getType();
|
||||||
if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
|
if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
|
||||||
return new BasicValue(arrayType.getElementType());
|
return new StrictBasicValue(arrayType.getElementType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.binaryOperation(insn, value1, value2);
|
switch (insn.getOpcode()) {
|
||||||
|
case IALOAD:
|
||||||
|
case BALOAD:
|
||||||
|
case CALOAD:
|
||||||
|
case SALOAD:
|
||||||
|
case IADD:
|
||||||
|
case ISUB:
|
||||||
|
case IMUL:
|
||||||
|
case IDIV:
|
||||||
|
case IREM:
|
||||||
|
case ISHL:
|
||||||
|
case ISHR:
|
||||||
|
case IUSHR:
|
||||||
|
case IAND:
|
||||||
|
case IOR:
|
||||||
|
case IXOR:
|
||||||
|
return StrictBasicValue.INT_VALUE;
|
||||||
|
case FALOAD:
|
||||||
|
case FADD:
|
||||||
|
case FSUB:
|
||||||
|
case FMUL:
|
||||||
|
case FDIV:
|
||||||
|
case FREM:
|
||||||
|
return StrictBasicValue.FLOAT_VALUE;
|
||||||
|
case LALOAD:
|
||||||
|
case LADD:
|
||||||
|
case LSUB:
|
||||||
|
case LMUL:
|
||||||
|
case LDIV:
|
||||||
|
case LREM:
|
||||||
|
case LSHL:
|
||||||
|
case LSHR:
|
||||||
|
case LUSHR:
|
||||||
|
case LAND:
|
||||||
|
case LOR:
|
||||||
|
case LXOR:
|
||||||
|
return StrictBasicValue.LONG_VALUE;
|
||||||
|
case DALOAD:
|
||||||
|
case DADD:
|
||||||
|
case DSUB:
|
||||||
|
case DMUL:
|
||||||
|
case DDIV:
|
||||||
|
case DREM:
|
||||||
|
return StrictBasicValue.DOUBLE_VALUE;
|
||||||
|
case AALOAD:
|
||||||
|
return StrictBasicValue.REFERENCE_VALUE;
|
||||||
|
case LCMP:
|
||||||
|
case FCMPL:
|
||||||
|
case FCMPG:
|
||||||
|
case DCMPL:
|
||||||
|
case DCMPG:
|
||||||
|
return StrictBasicValue.INT_VALUE;
|
||||||
|
case IF_ICMPEQ:
|
||||||
|
case IF_ICMPNE:
|
||||||
|
case IF_ICMPLT:
|
||||||
|
case IF_ICMPGE:
|
||||||
|
case IF_ICMPGT:
|
||||||
|
case IF_ICMPLE:
|
||||||
|
case IF_ACMPEQ:
|
||||||
|
case IF_ACMPNE:
|
||||||
|
case PUTFIELD:
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw new Error("Internal error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicValue ternaryOperation(
|
||||||
|
AbstractInsnNode insn, BasicValue value1, BasicValue value2, BasicValue value3
|
||||||
|
) throws AnalyzerException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicValue naryOperation(
|
||||||
|
AbstractInsnNode insn, List<? extends BasicValue> values
|
||||||
|
) throws AnalyzerException {
|
||||||
|
int opcode = insn.getOpcode();
|
||||||
|
if (opcode == MULTIANEWARRAY) {
|
||||||
|
return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc));
|
||||||
|
}
|
||||||
|
else if (opcode == INVOKEDYNAMIC) {
|
||||||
|
return newValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return newValue(Type.getReturnType(((MethodInsnNode) insn).desc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void returnOperation(
|
||||||
|
AbstractInsnNode insn, BasicValue value, BasicValue expected
|
||||||
|
) throws AnalyzerException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicValue unaryOperation(
|
||||||
|
AbstractInsnNode insn,
|
||||||
|
BasicValue value
|
||||||
|
) throws AnalyzerException {
|
||||||
|
switch (insn.getOpcode()) {
|
||||||
|
case INEG:
|
||||||
|
case IINC:
|
||||||
|
case L2I:
|
||||||
|
case F2I:
|
||||||
|
case D2I:
|
||||||
|
case I2B:
|
||||||
|
case I2C:
|
||||||
|
case I2S:
|
||||||
|
return StrictBasicValue.INT_VALUE;
|
||||||
|
case FNEG:
|
||||||
|
case I2F:
|
||||||
|
case L2F:
|
||||||
|
case D2F:
|
||||||
|
return StrictBasicValue.FLOAT_VALUE;
|
||||||
|
case LNEG:
|
||||||
|
case I2L:
|
||||||
|
case F2L:
|
||||||
|
case D2L:
|
||||||
|
return StrictBasicValue.LONG_VALUE;
|
||||||
|
case DNEG:
|
||||||
|
case I2D:
|
||||||
|
case L2D:
|
||||||
|
case F2D:
|
||||||
|
return StrictBasicValue.DOUBLE_VALUE;
|
||||||
|
case IFEQ:
|
||||||
|
case IFNE:
|
||||||
|
case IFLT:
|
||||||
|
case IFGE:
|
||||||
|
case IFGT:
|
||||||
|
case IFLE:
|
||||||
|
case TABLESWITCH:
|
||||||
|
case LOOKUPSWITCH:
|
||||||
|
case IRETURN:
|
||||||
|
case LRETURN:
|
||||||
|
case FRETURN:
|
||||||
|
case DRETURN:
|
||||||
|
case ARETURN:
|
||||||
|
case PUTSTATIC:
|
||||||
|
return null;
|
||||||
|
case GETFIELD:
|
||||||
|
return newValue(Type.getType(((FieldInsnNode) insn).desc));
|
||||||
|
case NEWARRAY:
|
||||||
|
switch (((IntInsnNode) insn).operand) {
|
||||||
|
case T_BOOLEAN:
|
||||||
|
return newValue(Type.getType("[Z"));
|
||||||
|
case T_CHAR:
|
||||||
|
return newValue(Type.getType("[C"));
|
||||||
|
case T_BYTE:
|
||||||
|
return newValue(Type.getType("[B"));
|
||||||
|
case T_SHORT:
|
||||||
|
return newValue(Type.getType("[S"));
|
||||||
|
case T_INT:
|
||||||
|
return newValue(Type.getType("[I"));
|
||||||
|
case T_FLOAT:
|
||||||
|
return newValue(Type.getType("[F"));
|
||||||
|
case T_DOUBLE:
|
||||||
|
return newValue(Type.getType("[D"));
|
||||||
|
case T_LONG:
|
||||||
|
return newValue(Type.getType("[J"));
|
||||||
|
default:
|
||||||
|
throw new AnalyzerException(insn, "Invalid array type");
|
||||||
|
}
|
||||||
|
case ANEWARRAY:
|
||||||
|
String desc = ((TypeInsnNode) insn).desc;
|
||||||
|
return newValue(Type.getType("[" + Type.getObjectType(desc)));
|
||||||
|
case ARRAYLENGTH:
|
||||||
|
return StrictBasicValue.INT_VALUE;
|
||||||
|
case ATHROW:
|
||||||
|
return null;
|
||||||
|
case CHECKCAST:
|
||||||
|
desc = ((TypeInsnNode) insn).desc;
|
||||||
|
return newValue(Type.getObjectType(desc));
|
||||||
|
case INSTANCEOF:
|
||||||
|
return StrictBasicValue.INT_VALUE;
|
||||||
|
case MONITORENTER:
|
||||||
|
case MONITOREXIT:
|
||||||
|
case IFNULL:
|
||||||
|
case IFNONNULL:
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw new Error("Internal error.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
class ImpulsMigration
|
||||||
|
{
|
||||||
|
fun migrate(oldVersion: Long)
|
||||||
|
{
|
||||||
|
var _oldVersion = oldVersion
|
||||||
|
|
||||||
|
if (4 > 3)
|
||||||
|
{
|
||||||
|
_oldVersion = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 > 3)
|
||||||
|
{
|
||||||
|
_oldVersion++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
ImpulsMigration().migrate(1L)
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+6
@@ -13900,6 +13900,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt14447.kt")
|
||||||
|
public void testKt14447() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/kt14447.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt1515.kt")
|
@TestMetadata("kt1515.kt")
|
||||||
public void testKt1515() throws Exception {
|
public void testKt1515() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/kt1515.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/kt1515.kt");
|
||||||
|
|||||||
@@ -13900,6 +13900,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt14447.kt")
|
||||||
|
public void testKt14447() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/kt14447.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt1515.kt")
|
@TestMetadata("kt1515.kt")
|
||||||
public void testKt1515() throws Exception {
|
public void testKt1515() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/kt1515.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/kt1515.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user