| // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| package com.android.tools.r8.lightir; |
| |
| import com.android.tools.r8.cf.code.CfNumberConversion; |
| import com.android.tools.r8.errors.Unimplemented; |
| import com.android.tools.r8.errors.Unreachable; |
| import com.android.tools.r8.graph.DexCallSite; |
| import com.android.tools.r8.graph.DexField; |
| import com.android.tools.r8.graph.DexItem; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexMethodHandle; |
| import com.android.tools.r8.graph.DexProto; |
| import com.android.tools.r8.graph.DexReference; |
| import com.android.tools.r8.graph.DexString; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.ir.code.IfType; |
| import com.android.tools.r8.ir.code.MemberType; |
| import com.android.tools.r8.ir.code.NumericType; |
| import com.android.tools.r8.lightir.LirBuilder.FillArrayPayload; |
| import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload; |
| import com.android.tools.r8.lightir.LirBuilder.NameComputationPayload; |
| import com.android.tools.r8.lightir.LirBuilder.RecordFieldValuesPayload; |
| import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload; |
| import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Structured callbacks for interpreting LIR. |
| * |
| * <p>This callback parses the actual instructions and dispatches to instruction specific methods |
| * where the parsed data is provided as arguments. Instructions that are part of a family of |
| * instructions have a default implementation that will call the "instruction family" methods (e.g., |
| * onInvokeVirtual will default dispatch to onInvokedMethodInstruction). |
| * |
| * <p>Due to the parsing of the individual instructions, this parser has a higher overhead than |
| * using the basic {@link LirInstructionView}. |
| */ |
| public abstract class LirParsedInstructionCallback<EV> implements LirInstructionCallback { |
| |
| private final LirCode<EV> code; |
| |
| public LirParsedInstructionCallback(LirCode<EV> code) { |
| this.code = code; |
| } |
| |
| /** Returns the index for the value associated with the current argument/instruction. */ |
| public abstract int getCurrentValueIndex(); |
| |
| public final int getCurrentInstructionIndex() { |
| return getCurrentValueIndex() - code.getArgumentCount(); |
| } |
| |
| private EV getActualValueIndex(int relativeValueIndex) { |
| return code.decodeValueIndex(relativeValueIndex, getCurrentValueIndex()); |
| } |
| |
| private EV getNextValueOperand(LirInstructionView view) { |
| return getActualValueIndex(view.getNextValueOperand()); |
| } |
| |
| private DexType getNextDexTypeOperand(LirInstructionView view) { |
| return (DexType) getConstantItem(view.getNextConstantOperand()); |
| } |
| |
| public void onInstruction() {} |
| |
| public void onConstNull() { |
| onInstruction(); |
| } |
| |
| public void onConstNumber(NumericType type, long value) { |
| onInstruction(); |
| } |
| |
| public void onConstInt(int value) { |
| onConstNumber(NumericType.INT, value); |
| } |
| |
| public void onConstFloat(int value) { |
| onConstNumber(NumericType.FLOAT, value); |
| } |
| |
| public void onConstLong(long value) { |
| onConstNumber(NumericType.LONG, value); |
| } |
| |
| public void onConstDouble(long value) { |
| onConstNumber(NumericType.DOUBLE, value); |
| } |
| |
| public void onConstString(DexString string) { |
| onInstruction(); |
| } |
| |
| public void onDexItemBasedConstString( |
| DexReference item, NameComputationInfo<?> nameComputationInfo) { |
| onInstruction(); |
| } |
| |
| public void onConstClass(DexType type, boolean ignoreCompatRules) { |
| onInstruction(); |
| } |
| |
| public void onConstMethodHandle(DexMethodHandle methodHandle) { |
| onInstruction(); |
| } |
| |
| public void onConstMethodType(DexProto methodType) { |
| onInstruction(); |
| } |
| |
| private void onArrayGetInternal(MemberType type, LirInstructionView view) { |
| EV array = getNextValueOperand(view); |
| EV index = getNextValueOperand(view); |
| onArrayGet(type, array, index); |
| } |
| |
| public void onArrayGet(MemberType type, EV array, EV index) { |
| onInstruction(); |
| } |
| |
| private void onArrayPutInternal(MemberType type, LirInstructionView view) { |
| EV array = getNextValueOperand(view); |
| EV index = getNextValueOperand(view); |
| EV value = getNextValueOperand(view); |
| onArrayPut(type, array, index, value); |
| } |
| |
| public void onArrayPut(MemberType type, EV array, EV index, EV value) { |
| onInstruction(); |
| } |
| |
| public void onBinop(NumericType type, EV left, EV right) { |
| onInstruction(); |
| } |
| |
| public void onArithmeticBinop(NumericType type, EV left, EV right) { |
| onBinop(type, left, right); |
| } |
| |
| public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) { |
| onArithmeticBinop(type, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onAddInt(EV leftValueIndex, EV rightValueIndex) { |
| onAdd(NumericType.INT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onAddLong(EV leftValueIndex, EV rightValueIndex) { |
| onAdd(NumericType.LONG, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onAddFloat(EV leftValueIndex, EV rightValueIndex) { |
| onAdd(NumericType.FLOAT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onAddDouble(EV leftValueIndex, EV rightValueIndex) { |
| onAdd(NumericType.DOUBLE, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) { |
| onArithmeticBinop(type, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onSubInt(EV leftValueIndex, EV rightValueIndex) { |
| onSub(NumericType.INT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onSubLong(EV leftValueIndex, EV rightValueIndex) { |
| onSub(NumericType.LONG, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onSubFloat(EV leftValueIndex, EV rightValueIndex) { |
| onSub(NumericType.FLOAT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onSubDouble(EV leftValueIndex, EV rightValueIndex) { |
| onSub(NumericType.DOUBLE, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onMul(NumericType type, EV leftValueIndex, EV rightValueIndex) { |
| onArithmeticBinop(type, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onMulInt(EV leftValueIndex, EV rightValueIndex) { |
| onMul(NumericType.INT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onMulLong(EV leftValueIndex, EV rightValueIndex) { |
| onMul(NumericType.LONG, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onMulFloat(EV leftValueIndex, EV rightValueIndex) { |
| onMul(NumericType.FLOAT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onMulDouble(EV leftValueIndex, EV rightValueIndex) { |
| onMul(NumericType.DOUBLE, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) { |
| onArithmeticBinop(type, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onDivInt(EV leftValueIndex, EV rightValueIndex) { |
| onDiv(NumericType.INT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onDivLong(EV leftValueIndex, EV rightValueIndex) { |
| onDiv(NumericType.LONG, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onDivFloat(EV leftValueIndex, EV rightValueIndex) { |
| onDiv(NumericType.FLOAT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onDivDouble(EV leftValueIndex, EV rightValueIndex) { |
| onDiv(NumericType.DOUBLE, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onRem(NumericType type, EV leftValueIndex, EV rightValueIndex) { |
| onArithmeticBinop(type, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onRemInt(EV leftValueIndex, EV rightValueIndex) { |
| onRem(NumericType.INT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onRemLong(EV leftValueIndex, EV rightValueIndex) { |
| onRem(NumericType.LONG, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onRemFloat(EV leftValueIndex, EV rightValueIndex) { |
| onRem(NumericType.FLOAT, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onRemDouble(EV leftValueIndex, EV rightValueIndex) { |
| onRem(NumericType.DOUBLE, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onNeg(NumericType type, EV value) { |
| onInstruction(); |
| } |
| |
| public void onNot(NumericType type, EV value) { |
| onInstruction(); |
| } |
| |
| private void onLogicalBinopInternal(int opcode, LirInstructionView view) { |
| EV left = getNextValueOperand(view); |
| EV right = getNextValueOperand(view); |
| switch (opcode) { |
| case LirOpcodes.ISHL: |
| onShl(NumericType.INT, left, right); |
| return; |
| case LirOpcodes.LSHL: |
| onShl(NumericType.LONG, left, right); |
| return; |
| case LirOpcodes.ISHR: |
| onShr(NumericType.INT, left, right); |
| return; |
| case LirOpcodes.LSHR: |
| onShr(NumericType.LONG, left, right); |
| return; |
| case LirOpcodes.IUSHR: |
| onUshr(NumericType.INT, left, right); |
| return; |
| case LirOpcodes.LUSHR: |
| onUshr(NumericType.LONG, left, right); |
| return; |
| case LirOpcodes.IAND: |
| onAnd(NumericType.INT, left, right); |
| return; |
| case LirOpcodes.LAND: |
| onAnd(NumericType.LONG, left, right); |
| return; |
| case LirOpcodes.IOR: |
| onOr(NumericType.INT, left, right); |
| return; |
| case LirOpcodes.LOR: |
| onOr(NumericType.LONG, left, right); |
| return; |
| case LirOpcodes.IXOR: |
| onXor(NumericType.INT, left, right); |
| return; |
| case LirOpcodes.LXOR: |
| onXor(NumericType.LONG, left, right); |
| return; |
| default: |
| throw new Unreachable("Unexpected logical binop: " + opcode); |
| } |
| } |
| |
| public void onLogicalBinop(NumericType type, EV left, EV right) { |
| onBinop(type, left, right); |
| } |
| |
| public void onShl(NumericType type, EV left, EV right) { |
| onLogicalBinop(type, left, right); |
| } |
| |
| public void onShr(NumericType type, EV left, EV right) { |
| onLogicalBinop(type, left, right); |
| } |
| |
| public void onUshr(NumericType type, EV left, EV right) { |
| onLogicalBinop(type, left, right); |
| } |
| |
| public void onAnd(NumericType type, EV left, EV right) { |
| onLogicalBinop(type, left, right); |
| } |
| |
| public void onOr(NumericType type, EV left, EV right) { |
| onLogicalBinop(type, left, right); |
| } |
| |
| public void onXor(NumericType type, EV left, EV right) { |
| onLogicalBinop(type, left, right); |
| } |
| |
| public void onNumberConversion(int opcode, EV value) { |
| assert LirOpcodes.I2L <= opcode; |
| assert opcode <= LirOpcodes.I2S; |
| CfNumberConversion insn = CfNumberConversion.fromAsm(opcode); |
| onNumberConversion(insn.getFromType(), insn.getToType(), value); |
| } |
| |
| public void onNumberConversion(NumericType from, NumericType to, EV value) { |
| onInstruction(); |
| } |
| |
| private void onIfInternal(IfType ifKind, LirInstructionView view) { |
| int blockIndex = view.getNextBlockOperand(); |
| EV valueIndex = getNextValueOperand(view); |
| onIf(ifKind, blockIndex, valueIndex); |
| } |
| |
| public void onIf(IfType ifKind, int blockIndex, EV valueIndex) { |
| onInstruction(); |
| } |
| |
| private void onIfCmpInternal(IfType ifKind, LirInstructionView view) { |
| int blockIndex = view.getNextBlockOperand(); |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onIfCmp(ifKind, blockIndex, leftValueIndex, rightValueIndex); |
| } |
| |
| public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) { |
| onInstruction(); |
| } |
| |
| public void onGoto(int blockIndex) { |
| onInstruction(); |
| } |
| |
| public void onIntSwitch(EV value, IntSwitchPayload payload) { |
| onInstruction(); |
| } |
| |
| public void onStringSwitch(EV value, StringSwitchPayload payload) { |
| onInstruction(); |
| } |
| |
| public void onFallthrough() { |
| onInstruction(); |
| } |
| |
| public void onMoveException(DexType exceptionType) { |
| onInstruction(); |
| } |
| |
| public void onDebugLocalWrite(EV srcIndex) { |
| onInstruction(); |
| } |
| |
| public void onDebugLocalRead() { |
| onInstruction(); |
| } |
| |
| public void onInvokeMultiNewArray(DexType type, List<EV> arguments) { |
| onInstruction(); |
| } |
| |
| public void onInvokeNewArray(DexType type, List<EV> arguments) { |
| onInstruction(); |
| } |
| |
| public void onNewArrayFilledData(int elementWidth, long size, short[] data, EV src) { |
| onInstruction(); |
| } |
| |
| public void onInvokeMethodInstruction(DexMethod method, List<EV> arguments) { |
| onInstruction(); |
| } |
| |
| public void onInvokeDirect(DexMethod method, List<EV> arguments, boolean isInterface) { |
| onInvokeMethodInstruction(method, arguments); |
| } |
| |
| public void onInvokeSuper(DexMethod method, List<EV> arguments, boolean isInterface) { |
| onInvokeMethodInstruction(method, arguments); |
| } |
| |
| public void onInvokeVirtual(DexMethod method, List<EV> arguments) { |
| onInvokeMethodInstruction(method, arguments); |
| } |
| |
| public void onInvokeStatic(DexMethod method, List<EV> arguments, boolean isInterface) { |
| onInvokeMethodInstruction(method, arguments); |
| } |
| |
| public void onInvokeInterface(DexMethod method, List<EV> arguments) { |
| onInvokeMethodInstruction(method, arguments); |
| } |
| |
| public void onInvokeCustom(DexCallSite callSite, List<EV> arguments) { |
| onInstruction(); |
| } |
| |
| public void onInvokePolymorphic(DexMethod target, DexProto proto, List<EV> arguments) { |
| onInstruction(); |
| } |
| |
| public void onNewInstance(DexType clazz) { |
| onInstruction(); |
| } |
| |
| public void onFieldInstruction(DexField field) { |
| onInstruction(); |
| } |
| |
| public void onStaticGet(DexField field) { |
| onFieldInstruction(field); |
| } |
| |
| public void onStaticPut(DexField field, EV value) { |
| onFieldInstruction(field); |
| } |
| |
| public void onInstanceGet(DexField field, EV object) { |
| onFieldInstruction(field); |
| } |
| |
| public void onInstancePut(DexField field, EV object, EV value) { |
| onFieldInstruction(field); |
| } |
| |
| public void onNewArrayEmpty(DexType type, EV size) { |
| onInstruction(); |
| } |
| |
| public void onThrow(EV exception) { |
| onInstruction(); |
| } |
| |
| public void onReturnVoid() { |
| onInstruction(); |
| } |
| |
| public void onReturn(EV value) { |
| onInstruction(); |
| } |
| |
| public void onArrayLength(EV arrayValueIndex) { |
| onInstruction(); |
| } |
| |
| public void onCheckCast(DexType type, EV value, boolean ignoreCompatRules) { |
| onInstruction(); |
| } |
| |
| public void onSafeCheckCast(DexType type, EV value) { |
| onInstruction(); |
| } |
| |
| public void onInstanceOf(DexType type, EV value) { |
| onInstruction(); |
| } |
| |
| public void onDebugPosition() { |
| onInstruction(); |
| } |
| |
| public void onPhi(List<EV> operands) { |
| onInstruction(); |
| } |
| |
| public void onCmpInstruction(int opcode, EV leftValue, EV rightValue) { |
| assert LirOpcodes.LCMP <= opcode && opcode <= LirOpcodes.DCMPG; |
| onInstruction(); |
| } |
| |
| public void onMonitorInstruction(EV value) { |
| onInstruction(); |
| } |
| |
| public void onMonitorEnter(EV value) { |
| onMonitorInstruction(value); |
| } |
| |
| public void onMonitorExit(EV value) { |
| onMonitorInstruction(value); |
| } |
| |
| public void onNewUnboxedEnumInstance(DexType type, int ordinal) { |
| onInstruction(); |
| } |
| |
| public void onInitClass(DexType clazz) { |
| onInstruction(); |
| } |
| |
| public void onRecordFieldValues(DexField[] fields, List<EV> values) { |
| onInstruction(); |
| } |
| |
| private DexItem getConstantItem(int index) { |
| return code.getConstantItem(index); |
| } |
| |
| @Override |
| public void onInstructionView(LirInstructionView view) { |
| int opcode = view.getOpcode(); |
| switch (opcode) { |
| case LirOpcodes.ACONST_NULL: |
| { |
| onConstNull(); |
| return; |
| } |
| case LirOpcodes.LDC: |
| { |
| DexItem item = getConstantItem(view.getNextConstantOperand()); |
| if (item instanceof DexString) { |
| onConstString((DexString) item); |
| return; |
| } |
| if (item instanceof DexType) { |
| onConstClass((DexType) item, false); |
| return; |
| } |
| if (item instanceof DexMethodHandle) { |
| onConstMethodHandle((DexMethodHandle) item); |
| return; |
| } |
| if (item instanceof DexProto) { |
| onConstMethodType((DexProto) item); |
| return; |
| } |
| throw new Unimplemented(); |
| } |
| case LirOpcodes.CONSTCLASS_IGNORE_COMPAT: |
| { |
| DexItem item = getConstantItem(view.getNextConstantOperand()); |
| onConstClass((DexType) item, true); |
| return; |
| } |
| case LirOpcodes.ICONST_M1: |
| case LirOpcodes.ICONST_0: |
| case LirOpcodes.ICONST_1: |
| case LirOpcodes.ICONST_2: |
| case LirOpcodes.ICONST_3: |
| case LirOpcodes.ICONST_4: |
| case LirOpcodes.ICONST_5: |
| { |
| int value = opcode - LirOpcodes.ICONST_0; |
| onConstInt(value); |
| return; |
| } |
| case LirOpcodes.ICONST: |
| { |
| int value = view.getNextIntegerOperand(); |
| onConstInt(value); |
| return; |
| } |
| case LirOpcodes.FCONST_0: |
| case LirOpcodes.FCONST_1: |
| case LirOpcodes.FCONST_2: |
| { |
| float value = opcode - LirOpcodes.FCONST_0; |
| onConstFloat(Float.floatToRawIntBits(value)); |
| return; |
| } |
| case LirOpcodes.FCONST: |
| { |
| int value = view.getNextIntegerOperand(); |
| onConstFloat(value); |
| return; |
| } |
| case LirOpcodes.LCONST_0: |
| { |
| onConstLong(0); |
| return; |
| } |
| case LirOpcodes.LCONST_1: |
| { |
| onConstLong(1); |
| return; |
| } |
| case LirOpcodes.LCONST: |
| { |
| long value = view.getNextLongOperand(); |
| onConstLong(value); |
| return; |
| } |
| case LirOpcodes.DCONST_0: |
| { |
| onConstDouble(Double.doubleToRawLongBits(0)); |
| return; |
| } |
| case LirOpcodes.DCONST_1: |
| { |
| onConstDouble(Double.doubleToRawLongBits(1)); |
| return; |
| } |
| case LirOpcodes.DCONST: |
| { |
| long value = view.getNextLongOperand(); |
| onConstDouble(value); |
| return; |
| } |
| case LirOpcodes.IALOAD: |
| { |
| onArrayGetInternal(MemberType.INT, view); |
| break; |
| } |
| case LirOpcodes.LALOAD: |
| { |
| onArrayGetInternal(MemberType.LONG, view); |
| break; |
| } |
| case LirOpcodes.FALOAD: |
| { |
| onArrayGetInternal(MemberType.FLOAT, view); |
| break; |
| } |
| case LirOpcodes.DALOAD: |
| { |
| onArrayGetInternal(MemberType.DOUBLE, view); |
| break; |
| } |
| case LirOpcodes.AALOAD: |
| { |
| onArrayGetInternal(MemberType.OBJECT, view); |
| break; |
| } |
| case LirOpcodes.BALOAD: |
| { |
| onArrayGetInternal(MemberType.BOOLEAN_OR_BYTE, view); |
| break; |
| } |
| case LirOpcodes.CALOAD: |
| { |
| onArrayGetInternal(MemberType.CHAR, view); |
| break; |
| } |
| case LirOpcodes.SALOAD: |
| { |
| onArrayGetInternal(MemberType.SHORT, view); |
| break; |
| } |
| case LirOpcodes.IASTORE: |
| { |
| onArrayPutInternal(MemberType.INT, view); |
| break; |
| } |
| case LirOpcodes.LASTORE: |
| { |
| onArrayPutInternal(MemberType.LONG, view); |
| break; |
| } |
| case LirOpcodes.FASTORE: |
| { |
| onArrayPutInternal(MemberType.FLOAT, view); |
| break; |
| } |
| |
| case LirOpcodes.DASTORE: |
| { |
| onArrayPutInternal(MemberType.DOUBLE, view); |
| break; |
| } |
| |
| case LirOpcodes.AASTORE: |
| { |
| onArrayPutInternal(MemberType.OBJECT, view); |
| break; |
| } |
| |
| case LirOpcodes.BASTORE: |
| { |
| onArrayPutInternal(MemberType.BOOLEAN_OR_BYTE, view); |
| break; |
| } |
| |
| case LirOpcodes.CASTORE: |
| { |
| onArrayPutInternal(MemberType.CHAR, view); |
| break; |
| } |
| |
| case LirOpcodes.SASTORE: |
| { |
| onArrayPutInternal(MemberType.SHORT, view); |
| break; |
| } |
| |
| case LirOpcodes.IADD: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onAddInt(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.LADD: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onAddLong(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.FADD: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onAddFloat(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.DADD: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onAddDouble(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.ISUB: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onSubInt(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.LSUB: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onSubLong(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.FSUB: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onSubFloat(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.DSUB: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onSubDouble(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.IMUL: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onMulInt(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.LMUL: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onMulLong(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.FMUL: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onMulFloat(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.DMUL: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onMulDouble(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.IDIV: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onDivInt(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.LDIV: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onDivLong(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.FDIV: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onDivFloat(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.DDIV: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onDivDouble(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.IREM: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onRemInt(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.LREM: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onRemLong(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.FREM: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onRemFloat(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.DREM: |
| { |
| EV leftValueIndex = getNextValueOperand(view); |
| EV rightValueIndex = getNextValueOperand(view); |
| onRemDouble(leftValueIndex, rightValueIndex); |
| return; |
| } |
| case LirOpcodes.INEG: |
| { |
| EV value = getNextValueOperand(view); |
| onNeg(NumericType.INT, value); |
| return; |
| } |
| case LirOpcodes.LNEG: |
| { |
| EV value = getNextValueOperand(view); |
| onNeg(NumericType.LONG, value); |
| return; |
| } |
| case LirOpcodes.FNEG: |
| { |
| EV value = getNextValueOperand(view); |
| onNeg(NumericType.FLOAT, value); |
| return; |
| } |
| case LirOpcodes.DNEG: |
| { |
| EV value = getNextValueOperand(view); |
| onNeg(NumericType.DOUBLE, value); |
| return; |
| } |
| case LirOpcodes.ISHL: |
| case LirOpcodes.LSHL: |
| case LirOpcodes.ISHR: |
| case LirOpcodes.LSHR: |
| case LirOpcodes.IUSHR: |
| case LirOpcodes.LUSHR: |
| case LirOpcodes.IAND: |
| case LirOpcodes.LAND: |
| case LirOpcodes.IOR: |
| case LirOpcodes.LOR: |
| case LirOpcodes.IXOR: |
| case LirOpcodes.LXOR: |
| { |
| onLogicalBinopInternal(opcode, view); |
| return; |
| } |
| case LirOpcodes.I2L: |
| case LirOpcodes.I2F: |
| case LirOpcodes.I2D: |
| case LirOpcodes.L2I: |
| case LirOpcodes.L2F: |
| case LirOpcodes.L2D: |
| case LirOpcodes.F2I: |
| case LirOpcodes.F2L: |
| case LirOpcodes.F2D: |
| case LirOpcodes.D2I: |
| case LirOpcodes.D2L: |
| case LirOpcodes.D2F: |
| case LirOpcodes.I2B: |
| case LirOpcodes.I2C: |
| case LirOpcodes.I2S: |
| { |
| EV value = getNextValueOperand(view); |
| onNumberConversion(opcode, value); |
| return; |
| } |
| case LirOpcodes.IFEQ: |
| { |
| onIfInternal(IfType.EQ, view); |
| return; |
| } |
| case LirOpcodes.IFNE: |
| { |
| onIfInternal(IfType.NE, view); |
| return; |
| } |
| case LirOpcodes.IFLT: |
| { |
| onIfInternal(IfType.LT, view); |
| return; |
| } |
| case LirOpcodes.IFGE: |
| { |
| onIfInternal(IfType.GE, view); |
| return; |
| } |
| case LirOpcodes.IFGT: |
| { |
| onIfInternal(IfType.GT, view); |
| return; |
| } |
| case LirOpcodes.IFLE: |
| { |
| onIfInternal(IfType.LE, view); |
| return; |
| } |
| case LirOpcodes.IFNULL: |
| { |
| onIfInternal(IfType.EQ, view); |
| return; |
| } |
| case LirOpcodes.IFNONNULL: |
| { |
| onIfInternal(IfType.NE, view); |
| return; |
| } |
| case LirOpcodes.IF_ICMPEQ: |
| { |
| onIfCmpInternal(IfType.EQ, view); |
| return; |
| } |
| case LirOpcodes.IF_ICMPNE: |
| { |
| onIfCmpInternal(IfType.NE, view); |
| return; |
| } |
| case LirOpcodes.IF_ICMPLT: |
| { |
| onIfCmpInternal(IfType.LT, view); |
| return; |
| } |
| case LirOpcodes.IF_ICMPGE: |
| { |
| onIfCmpInternal(IfType.GE, view); |
| return; |
| } |
| case LirOpcodes.IF_ICMPGT: |
| { |
| onIfCmpInternal(IfType.GT, view); |
| return; |
| } |
| case LirOpcodes.IF_ICMPLE: |
| { |
| onIfCmpInternal(IfType.LE, view); |
| return; |
| } |
| case LirOpcodes.IF_ACMPEQ: |
| { |
| onIfCmpInternal(IfType.EQ, view); |
| return; |
| } |
| case LirOpcodes.IF_ACMPNE: |
| { |
| onIfCmpInternal(IfType.NE, view); |
| return; |
| } |
| case LirOpcodes.GOTO: |
| { |
| int blockIndex = view.getNextBlockOperand(); |
| onGoto(blockIndex); |
| return; |
| } |
| case LirOpcodes.TABLESWITCH: |
| { |
| IntSwitchPayload payload = |
| (IntSwitchPayload) getConstantItem(view.getNextConstantOperand()); |
| EV value = getNextValueOperand(view); |
| onIntSwitch(value, payload); |
| return; |
| } |
| case LirOpcodes.STRINGSWITCH: |
| { |
| StringSwitchPayload payload = |
| (StringSwitchPayload) getConstantItem(view.getNextConstantOperand()); |
| EV value = getNextValueOperand(view); |
| onStringSwitch(value, payload); |
| return; |
| } |
| case LirOpcodes.INVOKEDIRECT: |
| case LirOpcodes.INVOKEDIRECT_ITF: |
| { |
| DexMethod target = getInvokeInstructionTarget(view); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokeDirect(target, arguments, opcode == LirOpcodes.INVOKEDIRECT_ITF); |
| return; |
| } |
| case LirOpcodes.INVOKESUPER: |
| case LirOpcodes.INVOKESUPER_ITF: |
| { |
| DexMethod target = getInvokeInstructionTarget(view); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokeSuper(target, arguments, opcode == LirOpcodes.INVOKESUPER_ITF); |
| return; |
| } |
| case LirOpcodes.INVOKEVIRTUAL: |
| { |
| DexMethod target = getInvokeInstructionTarget(view); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokeVirtual(target, arguments); |
| return; |
| } |
| case LirOpcodes.INVOKESTATIC: |
| case LirOpcodes.INVOKESTATIC_ITF: |
| { |
| DexMethod target = getInvokeInstructionTarget(view); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokeStatic(target, arguments, opcode == LirOpcodes.INVOKESTATIC_ITF); |
| return; |
| } |
| case LirOpcodes.INVOKEINTERFACE: |
| { |
| DexMethod target = getInvokeInstructionTarget(view); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokeInterface(target, arguments); |
| return; |
| } |
| case LirOpcodes.INVOKEDYNAMIC: |
| { |
| DexCallSite callSite = (DexCallSite) getConstantItem(view.getNextConstantOperand()); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokeCustom(callSite, arguments); |
| return; |
| } |
| case LirOpcodes.INVOKEPOLYMORPHIC: |
| { |
| DexMethod target = getInvokeInstructionTarget(view); |
| DexProto proto = (DexProto) getConstantItem(view.getNextConstantOperand()); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokePolymorphic(target, proto, arguments); |
| return; |
| } |
| case LirOpcodes.NEW: |
| { |
| DexItem item = getConstantItem(view.getNextConstantOperand()); |
| if (item instanceof DexType) { |
| onNewInstance((DexType) item); |
| return; |
| } |
| throw new Unimplemented(); |
| } |
| case LirOpcodes.GETSTATIC: |
| { |
| DexField field = (DexField) getConstantItem(view.getNextConstantOperand()); |
| onStaticGet(field); |
| return; |
| } |
| case LirOpcodes.PUTSTATIC: |
| { |
| DexField field = (DexField) getConstantItem(view.getNextConstantOperand()); |
| EV value = getNextValueOperand(view); |
| onStaticPut(field, value); |
| return; |
| } |
| case LirOpcodes.GETFIELD: |
| { |
| DexField field = (DexField) getConstantItem(view.getNextConstantOperand()); |
| EV object = getNextValueOperand(view); |
| onInstanceGet(field, object); |
| return; |
| } |
| case LirOpcodes.PUTFIELD: |
| { |
| DexField field = (DexField) getConstantItem(view.getNextConstantOperand()); |
| EV object = getNextValueOperand(view); |
| EV value = getNextValueOperand(view); |
| onInstancePut(field, object, value); |
| return; |
| } |
| case LirOpcodes.NEWARRAY: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| EV size = getNextValueOperand(view); |
| onNewArrayEmpty(type, size); |
| return; |
| } |
| case LirOpcodes.ATHROW: |
| { |
| EV exception = getNextValueOperand(view); |
| onThrow(exception); |
| return; |
| } |
| case LirOpcodes.RETURN: |
| { |
| onReturnVoid(); |
| return; |
| } |
| case LirOpcodes.ARETURN: |
| { |
| EV value = getNextValueOperand(view); |
| onReturn(value); |
| return; |
| } |
| case LirOpcodes.ARRAYLENGTH: |
| { |
| onArrayLength(getNextValueOperand(view)); |
| return; |
| } |
| case LirOpcodes.CHECKCAST: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| EV value = getNextValueOperand(view); |
| onCheckCast(type, value, false); |
| return; |
| } |
| case LirOpcodes.CHECKCAST_IGNORE_COMPAT: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| EV value = getNextValueOperand(view); |
| onCheckCast(type, value, true); |
| return; |
| } |
| case LirOpcodes.CHECKCAST_SAFE: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| EV value = getNextValueOperand(view); |
| onSafeCheckCast(type, value); |
| return; |
| } |
| case LirOpcodes.INSTANCEOF: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| EV value = getNextValueOperand(view); |
| onInstanceOf(type, value); |
| return; |
| } |
| case LirOpcodes.DEBUGPOS: |
| { |
| onDebugPosition(); |
| return; |
| } |
| case LirOpcodes.PHI: |
| { |
| List<EV> operands = new ArrayList<>(); |
| while (view.hasMoreOperands()) { |
| operands.add(getNextValueOperand(view)); |
| } |
| onPhi(operands); |
| return; |
| } |
| case LirOpcodes.FALLTHROUGH: |
| { |
| onFallthrough(); |
| return; |
| } |
| case LirOpcodes.MOVEEXCEPTION: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| onMoveException(type); |
| return; |
| } |
| case LirOpcodes.MONITORENTER: |
| { |
| EV value = getNextValueOperand(view); |
| onMonitorEnter(value); |
| return; |
| } |
| case LirOpcodes.MONITOREXIT: |
| { |
| EV value = getNextValueOperand(view); |
| onMonitorExit(value); |
| return; |
| } |
| case LirOpcodes.DEBUGLOCALWRITE: |
| { |
| EV srcIndex = getNextValueOperand(view); |
| onDebugLocalWrite(srcIndex); |
| return; |
| } |
| case LirOpcodes.DEBUGLOCALREAD: |
| { |
| onDebugLocalRead(); |
| return; |
| } |
| case LirOpcodes.MULTIANEWARRAY: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokeMultiNewArray(type, arguments); |
| return; |
| } |
| case LirOpcodes.INVOKENEWARRAY: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| List<EV> arguments = getInvokeInstructionArguments(view); |
| onInvokeNewArray(type, arguments); |
| return; |
| } |
| case LirOpcodes.NEWARRAYFILLEDDATA: |
| { |
| FillArrayPayload payload = |
| (FillArrayPayload) getConstantItem(view.getNextConstantOperand()); |
| EV src = getNextValueOperand(view); |
| onNewArrayFilledData(payload.element_width, payload.size, payload.data, src); |
| return; |
| } |
| case LirOpcodes.LCMP: |
| case LirOpcodes.FCMPL: |
| case LirOpcodes.FCMPG: |
| case LirOpcodes.DCMPL: |
| case LirOpcodes.DCMPG: |
| { |
| EV leftValue = getNextValueOperand(view); |
| EV rightValue = getNextValueOperand(view); |
| onCmpInstruction(opcode, leftValue, rightValue); |
| return; |
| } |
| case LirOpcodes.ITEMBASEDCONSTSTRING: |
| { |
| DexReference item = (DexReference) getConstantItem(view.getNextConstantOperand()); |
| NameComputationPayload payload = |
| (NameComputationPayload) getConstantItem(view.getNextConstantOperand()); |
| onDexItemBasedConstString(item, payload.nameComputationInfo); |
| return; |
| } |
| case LirOpcodes.NEWUNBOXEDENUMINSTANCE: |
| { |
| DexType type = getNextDexTypeOperand(view); |
| int ordinal = view.getNextIntegerOperand(); |
| onNewUnboxedEnumInstance(type, ordinal); |
| return; |
| } |
| case LirOpcodes.INOT: |
| case LirOpcodes.LNOT: |
| { |
| EV value = getNextValueOperand(view); |
| onNot(opcode == LirOpcodes.INOT ? NumericType.INT : NumericType.LONG, value); |
| return; |
| } |
| case LirOpcodes.INITCLASS: |
| { |
| DexType clazz = getNextDexTypeOperand(view); |
| onInitClass(clazz); |
| return; |
| } |
| case LirOpcodes.RECORDFIELDVALUES: |
| { |
| RecordFieldValuesPayload payload = |
| (RecordFieldValuesPayload) getConstantItem(view.getNextConstantOperand()); |
| List<EV> values = getInvokeInstructionArguments(view); |
| onRecordFieldValues(payload.fields, values); |
| return; |
| } |
| default: |
| throw new Unimplemented("No dispatch for opcode " + LirOpcodes.toString(opcode)); |
| } |
| } |
| |
| private DexMethod getInvokeInstructionTarget(LirInstructionView view) { |
| return (DexMethod) getConstantItem(view.getNextConstantOperand()); |
| } |
| |
| private List<EV> getInvokeInstructionArguments(LirInstructionView view) { |
| List<EV> arguments = new ArrayList<>(); |
| while (view.hasMoreOperands()) { |
| arguments.add(getNextValueOperand(view)); |
| } |
| return arguments; |
| } |
| } |