|  | // 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.errors.Unimplemented; | 
|  | import com.android.tools.r8.errors.Unreachable; | 
|  | import com.android.tools.r8.graph.AppView; | 
|  | import com.android.tools.r8.graph.DexCallSite; | 
|  | import com.android.tools.r8.graph.DexField; | 
|  | 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.graph.ProgramMethod; | 
|  | import com.android.tools.r8.graph.proto.ArgumentInfo; | 
|  | import com.android.tools.r8.graph.proto.ArgumentInfoCollection; | 
|  | import com.android.tools.r8.graph.proto.RemovedArgumentInfo; | 
|  | import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription; | 
|  | import com.android.tools.r8.graph.proto.RewrittenTypeInfo; | 
|  | import com.android.tools.r8.ir.analysis.type.Nullability; | 
|  | import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement; | 
|  | import com.android.tools.r8.ir.analysis.type.TypeAnalysis; | 
|  | import com.android.tools.r8.ir.analysis.type.TypeElement; | 
|  | import com.android.tools.r8.ir.code.Add; | 
|  | import com.android.tools.r8.ir.code.And; | 
|  | import com.android.tools.r8.ir.code.Argument; | 
|  | import com.android.tools.r8.ir.code.ArrayGet; | 
|  | import com.android.tools.r8.ir.code.ArrayLength; | 
|  | import com.android.tools.r8.ir.code.ArrayPut; | 
|  | import com.android.tools.r8.ir.code.BasicBlock; | 
|  | import com.android.tools.r8.ir.code.CatchHandlers; | 
|  | import com.android.tools.r8.ir.code.CheckCast; | 
|  | import com.android.tools.r8.ir.code.Cmp; | 
|  | import com.android.tools.r8.ir.code.Cmp.Bias; | 
|  | import com.android.tools.r8.ir.code.ConstClass; | 
|  | import com.android.tools.r8.ir.code.ConstMethodHandle; | 
|  | import com.android.tools.r8.ir.code.ConstMethodType; | 
|  | import com.android.tools.r8.ir.code.ConstNumber; | 
|  | import com.android.tools.r8.ir.code.ConstString; | 
|  | import com.android.tools.r8.ir.code.DebugLocalRead; | 
|  | import com.android.tools.r8.ir.code.DebugLocalWrite; | 
|  | import com.android.tools.r8.ir.code.DebugPosition; | 
|  | import com.android.tools.r8.ir.code.DexItemBasedConstString; | 
|  | import com.android.tools.r8.ir.code.Div; | 
|  | import com.android.tools.r8.ir.code.Goto; | 
|  | import com.android.tools.r8.ir.code.IRCode; | 
|  | import com.android.tools.r8.ir.code.If; | 
|  | import com.android.tools.r8.ir.code.IfType; | 
|  | import com.android.tools.r8.ir.code.InitClass; | 
|  | import com.android.tools.r8.ir.code.InstanceGet; | 
|  | import com.android.tools.r8.ir.code.InstanceOf; | 
|  | import com.android.tools.r8.ir.code.InstancePut; | 
|  | import com.android.tools.r8.ir.code.Instruction; | 
|  | import com.android.tools.r8.ir.code.IntSwitch; | 
|  | import com.android.tools.r8.ir.code.InvokeCustom; | 
|  | import com.android.tools.r8.ir.code.InvokeDirect; | 
|  | import com.android.tools.r8.ir.code.InvokeInterface; | 
|  | import com.android.tools.r8.ir.code.InvokeMultiNewArray; | 
|  | import com.android.tools.r8.ir.code.InvokePolymorphic; | 
|  | import com.android.tools.r8.ir.code.InvokeStatic; | 
|  | import com.android.tools.r8.ir.code.InvokeSuper; | 
|  | import com.android.tools.r8.ir.code.InvokeVirtual; | 
|  | import com.android.tools.r8.ir.code.MemberType; | 
|  | import com.android.tools.r8.ir.code.Monitor; | 
|  | import com.android.tools.r8.ir.code.MonitorType; | 
|  | import com.android.tools.r8.ir.code.MoveException; | 
|  | import com.android.tools.r8.ir.code.Mul; | 
|  | import com.android.tools.r8.ir.code.Neg; | 
|  | import com.android.tools.r8.ir.code.NewArrayEmpty; | 
|  | import com.android.tools.r8.ir.code.NewArrayFilled; | 
|  | import com.android.tools.r8.ir.code.NewArrayFilledData; | 
|  | import com.android.tools.r8.ir.code.NewInstance; | 
|  | import com.android.tools.r8.ir.code.NewUnboxedEnumInstance; | 
|  | import com.android.tools.r8.ir.code.Not; | 
|  | import com.android.tools.r8.ir.code.NumberConversion; | 
|  | import com.android.tools.r8.ir.code.NumberGenerator; | 
|  | import com.android.tools.r8.ir.code.NumericType; | 
|  | import com.android.tools.r8.ir.code.Or; | 
|  | import com.android.tools.r8.ir.code.Phi; | 
|  | import com.android.tools.r8.ir.code.Position; | 
|  | import com.android.tools.r8.ir.code.RecordFieldValues; | 
|  | import com.android.tools.r8.ir.code.Rem; | 
|  | import com.android.tools.r8.ir.code.Return; | 
|  | import com.android.tools.r8.ir.code.SafeCheckCast; | 
|  | import com.android.tools.r8.ir.code.Shl; | 
|  | import com.android.tools.r8.ir.code.Shr; | 
|  | import com.android.tools.r8.ir.code.StaticGet; | 
|  | import com.android.tools.r8.ir.code.StaticPut; | 
|  | import com.android.tools.r8.ir.code.StringSwitch; | 
|  | import com.android.tools.r8.ir.code.Sub; | 
|  | import com.android.tools.r8.ir.code.Throw; | 
|  | import com.android.tools.r8.ir.code.Ushr; | 
|  | import com.android.tools.r8.ir.code.Value; | 
|  | import com.android.tools.r8.ir.code.ValueType; | 
|  | import com.android.tools.r8.ir.code.Xor; | 
|  | import com.android.tools.r8.ir.conversion.ExtraParameter; | 
|  | import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; | 
|  | import com.android.tools.r8.ir.conversion.StringSwitchConverter; | 
|  | import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload; | 
|  | import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload; | 
|  | import com.android.tools.r8.lightir.LirCode.PositionEntry; | 
|  | import com.android.tools.r8.lightir.LirCode.TryCatchTable; | 
|  | import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo; | 
|  | import com.android.tools.r8.utils.ListUtils; | 
|  | import com.google.common.collect.ImmutableList; | 
|  | import it.unimi.dsi.fastutil.ints.Int2IntMap; | 
|  | import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; | 
|  | import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; | 
|  | import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; | 
|  | import it.unimi.dsi.fastutil.ints.IntArrayList; | 
|  | import it.unimi.dsi.fastutil.ints.IntList; | 
|  | import java.util.ArrayList; | 
|  | import java.util.LinkedList; | 
|  | import java.util.List; | 
|  | import java.util.function.BiFunction; | 
|  |  | 
|  | public class Lir2IRConverter { | 
|  |  | 
|  | private Lir2IRConverter() {} | 
|  |  | 
|  | public static <EV> IRCode translate( | 
|  | ProgramMethod method, | 
|  | LirCode<EV> lirCode, | 
|  | LirDecodingStrategy<Value, EV> strategy, | 
|  | AppView<?> appView, | 
|  | Position callerPosition, | 
|  | RewrittenPrototypeDescription protoChanges, | 
|  | MutableMethodConversionOptions conversionOptions) { | 
|  | Parser<EV> parser = | 
|  | new Parser<>( | 
|  | lirCode, | 
|  | method.getReference(), | 
|  | method.getDefinition().isD8R8Synthesized(), | 
|  | appView, | 
|  | strategy, | 
|  | callerPosition, | 
|  | protoChanges); | 
|  | parser.parseArguments(method); | 
|  | parser.ensureDebugInfo(); | 
|  | lirCode.forEach(view -> view.accept(parser)); | 
|  | IRCode irCode = parser.getIRCode(method, conversionOptions); | 
|  | // Some instructions have bottom types (e.g., phis). Compute their actual types by widening. | 
|  | new TypeAnalysis(appView, irCode).widening(); | 
|  |  | 
|  | if (conversionOptions.isStringSwitchConversionEnabled()) { | 
|  | if (StringSwitchConverter.convertToStringSwitchInstructions( | 
|  | irCode, appView.dexItemFactory())) { | 
|  | irCode.removeRedundantBlocks(); | 
|  | } | 
|  | } | 
|  | return irCode; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * When building IR the structured LIR parser is used to obtain the decoded operand indexes. The | 
|  | * below parser subclass handles translation of indexes to SSA values. | 
|  | */ | 
|  | private static class Parser<EV> extends LirParsedInstructionCallback<EV> { | 
|  |  | 
|  | private static final int ENTRY_BLOCK_INDEX = -1; | 
|  |  | 
|  | private final AppView<?> appView; | 
|  | private final LirCode<EV> code; | 
|  | private final DexMethod method; | 
|  | private final LirDecodingStrategy<Value, EV> strategy; | 
|  | private final NumberGenerator basicBlockNumberGenerator = new NumberGenerator(); | 
|  | private final RewrittenPrototypeDescription protoChanges; | 
|  |  | 
|  | private final Int2ReferenceMap<BasicBlock> blocks = new Int2ReferenceOpenHashMap<>(); | 
|  |  | 
|  | private BasicBlock currentBlock = null; | 
|  | private int nextInstructionIndex = 0; | 
|  |  | 
|  | private final Position entryPosition; | 
|  | private Position currentPosition; | 
|  | private PositionEntry nextPositionEntry = null; | 
|  | private int nextIndexInPositionsTable = 0; | 
|  | private final PositionEntry[] positionTable; | 
|  |  | 
|  | private final boolean buildForInlining; | 
|  |  | 
|  | public Parser( | 
|  | LirCode<EV> code, | 
|  | DexMethod method, | 
|  | boolean isD8R8Synthesized, | 
|  | AppView<?> appView, | 
|  | LirDecodingStrategy<Value, EV> strategy, | 
|  | Position callerPosition, | 
|  | RewrittenPrototypeDescription protoChanges) { | 
|  | super(code); | 
|  | this.appView = appView; | 
|  | this.code = code; | 
|  | this.method = method; | 
|  | this.strategy = strategy; | 
|  | this.protoChanges = protoChanges; | 
|  | assert protoChanges != null; | 
|  | if (callerPosition == null) { | 
|  | buildForInlining = false; | 
|  | positionTable = code.getPositionTable(); | 
|  | // Recreate the preamble position. This is active for arguments and code with no positions. | 
|  | currentPosition = code.getPreamblePosition(method, isD8R8Synthesized); | 
|  | } else { | 
|  | buildForInlining = true; | 
|  | positionTable = | 
|  | code.getPositionTableAsInlining( | 
|  | callerPosition, method, isD8R8Synthesized, preamble -> currentPosition = preamble); | 
|  | } | 
|  | entryPosition = currentPosition; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int getCurrentValueIndex() { | 
|  | return nextInstructionIndex + code.getArgumentCount(); | 
|  | } | 
|  |  | 
|  | private void closeCurrentBlock() { | 
|  | currentBlock = null; | 
|  | } | 
|  |  | 
|  | private void ensureCurrentBlock() { | 
|  | // Control instructions must close the block, thus the current block is null iff the | 
|  | // instruction denotes a new block. | 
|  | if (currentBlock == null) { | 
|  | currentBlock = getBasicBlock(nextInstructionIndex); | 
|  | TryCatchTable tryCatchTable = code.getTryCatchTable(); | 
|  | if (tryCatchTable != null) { | 
|  | CatchHandlers<Integer> handlers = tryCatchTable.getHandlersForBlock(nextInstructionIndex); | 
|  | if (handlers != null) { | 
|  | List<BasicBlock> targets = ListUtils.map(handlers.getAllTargets(), this::getBasicBlock); | 
|  | targets.forEach(currentBlock::link); | 
|  | currentBlock.linkCatchSuccessors(handlers.getGuards(), targets); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | assert !blocks.containsKey(nextInstructionIndex); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void ensureCurrentPosition() { | 
|  | if (nextPositionEntry != null | 
|  | && nextPositionEntry.getFromInstructionIndex() <= nextInstructionIndex) { | 
|  | currentPosition = | 
|  | nextPositionEntry.getPosition( | 
|  | method, entryPosition.getOutermostCaller().isD8R8Synthesized()); | 
|  | advanceNextPositionEntry(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void advanceNextPositionEntry() { | 
|  | nextPositionEntry = | 
|  | nextIndexInPositionsTable < positionTable.length | 
|  | ? positionTable[nextIndexInPositionsTable++] | 
|  | : null; | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public void parseArguments(ProgramMethod method) { | 
|  | ArgumentInfoCollection argumentsInfo = protoChanges.getArgumentInfoCollection(); | 
|  | currentBlock = getBasicBlock(ENTRY_BLOCK_INDEX); | 
|  | boolean hasReceiverArgument = !method.getDefinition().isStatic(); | 
|  |  | 
|  | int index = 0; | 
|  | if (hasReceiverArgument) { | 
|  | assert argumentsInfo.getNewArgumentIndex(0) == 0; | 
|  | addThisArgument(method.getHolderType()); | 
|  | index++; | 
|  | } | 
|  |  | 
|  | int originalNumberOfArguments = | 
|  | method.getParameters().size() | 
|  | + argumentsInfo.numberOfRemovedArguments() | 
|  | + method.getDefinition().getFirstNonReceiverArgumentIndex() | 
|  | - protoChanges.numberOfExtraParameters(); | 
|  |  | 
|  | int numberOfRemovedArguments = 0; | 
|  | while (index < originalNumberOfArguments) { | 
|  | ArgumentInfo argumentInfo = argumentsInfo.getArgumentInfo(index); | 
|  | if (argumentInfo.isRemovedArgumentInfo()) { | 
|  | RemovedArgumentInfo removedArgumentInfo = argumentInfo.asRemovedArgumentInfo(); | 
|  | addNonThisArgument(removedArgumentInfo.getType(), index++); | 
|  | numberOfRemovedArguments++; | 
|  | } else if (argumentInfo.isRewrittenTypeInfo()) { | 
|  | RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo(); | 
|  | int newArgumentIndex = argumentsInfo.getNewArgumentIndex(index, numberOfRemovedArguments); | 
|  | assert method.getArgumentType(newArgumentIndex) == rewrittenTypeInfo.getNewType(); | 
|  | addNonThisArgument(rewrittenTypeInfo.getOldType(), index++); | 
|  | } else { | 
|  | int newArgumentIndex = argumentsInfo.getNewArgumentIndex(index, numberOfRemovedArguments); | 
|  | addNonThisArgument(method.getArgumentType(newArgumentIndex), index++); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (ExtraParameter extraParameter : protoChanges.getExtraParameters()) { | 
|  | int newArgumentIndex = argumentsInfo.getNewArgumentIndex(index, numberOfRemovedArguments); | 
|  | DexType extraArgumentType = method.getArgumentType(newArgumentIndex); | 
|  | if (extraParameter.isUnused()) { | 
|  | // Note that we do *not* increment the index here as that would shift the SSA value map. | 
|  | addUnusedArgument(extraArgumentType); | 
|  | } else { | 
|  | addNonThisArgument(extraArgumentType, index++); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Set up position state after adding arguments. | 
|  | advanceNextPositionEntry(); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public void ensureDebugInfo() { | 
|  | if (code.getDebugLocalInfoTable() == null) { | 
|  | return; | 
|  | } | 
|  | code.getDebugLocalInfoTable() | 
|  | .forEachLocalDefinition( | 
|  | (encodedValue, localInfo) -> { | 
|  | Value value = getValue(encodedValue); | 
|  | if (!value.hasLocalInfo()) { | 
|  | value.setLocalInfo(localInfo); | 
|  | } | 
|  | assert value.getLocalInfo() == localInfo; | 
|  | }); | 
|  | } | 
|  |  | 
|  | // TODO(b/270398965): Replace LinkedList. | 
|  | @SuppressWarnings("JdkObsolete") | 
|  | public IRCode getIRCode( | 
|  | ProgramMethod method, MutableMethodConversionOptions conversionOptions) { | 
|  | LinkedList<BasicBlock> blockList = new LinkedList<>(); | 
|  | IntList blockIndices = new IntArrayList(blocks.keySet()); | 
|  | blockIndices.sort(Integer::compare); | 
|  | for (int i = 0; i < blockIndices.size(); i++) { | 
|  | BasicBlock block = blocks.get(blockIndices.getInt(i)); | 
|  | block.setFilled(); | 
|  | blockList.add(block); | 
|  | // LIR has no value-user info so after building is done, removed unused values. | 
|  | for (Instruction instruction : block.getInstructions()) { | 
|  | if (instruction.hasOutValue() | 
|  | && instruction.isInvoke() | 
|  | && instruction.hasUnusedOutValue()) { | 
|  | instruction.clearOutValue(); | 
|  | } | 
|  | } | 
|  | } | 
|  | return new IRCode( | 
|  | appView.options(), | 
|  | method, | 
|  | entryPosition, | 
|  | blockList, | 
|  | strategy.getValueNumberGenerator(), | 
|  | basicBlockNumberGenerator, | 
|  | code.getMetadataForIR(), | 
|  | method.getOrigin(), | 
|  | conversionOptions); | 
|  | } | 
|  |  | 
|  | public BasicBlock getBasicBlock(int instructionIndex) { | 
|  | return blocks.computeIfAbsent( | 
|  | instructionIndex, | 
|  | k -> { | 
|  | BasicBlock block = new BasicBlock(); | 
|  | block.setNumber(basicBlockNumberGenerator.next()); | 
|  | return block; | 
|  | }); | 
|  | } | 
|  |  | 
|  | public Value getValue(EV encodedValue) { | 
|  | return strategy.getValue(encodedValue, code.getStrategyInfo()); | 
|  | } | 
|  |  | 
|  | public List<Value> getValues(List<EV> indices) { | 
|  | List<Value> arguments = new ArrayList<>(indices.size()); | 
|  | for (int i = 0; i < indices.size(); i++) { | 
|  | arguments.add(getValue(indices.get(i))); | 
|  | } | 
|  | return arguments; | 
|  | } | 
|  |  | 
|  | public int toInstructionIndexInIR(int lirIndex) { | 
|  | return lirIndex + code.getArgumentCount(); | 
|  | } | 
|  |  | 
|  | public int peekNextInstructionIndex() { | 
|  | return nextInstructionIndex; | 
|  | } | 
|  |  | 
|  | public Value getOutValueForNextInstruction(TypeElement type) { | 
|  | int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex()); | 
|  | return strategy.getValueDefinitionForInstructionIndex( | 
|  | valueIndex, type, code::getDebugLocalInfo); | 
|  | } | 
|  |  | 
|  | public Phi getPhiForNextInstructionAndAdvanceState(TypeElement type) { | 
|  | int instructionIndex = peekNextInstructionIndex(); | 
|  | int valueIndex = toInstructionIndexInIR(instructionIndex); | 
|  | Phi phi = | 
|  | strategy.getPhiDefinitionForInstructionIndex( | 
|  | valueIndex, | 
|  | blockIndex -> getBasicBlockOrEnsureCurrentBlock(blockIndex, instructionIndex), | 
|  | type, | 
|  | code::getDebugLocalInfo, | 
|  | code.getStrategyInfo()); | 
|  | ensureCurrentPosition(); | 
|  | ++nextInstructionIndex; | 
|  | return phi; | 
|  | } | 
|  |  | 
|  | private BasicBlock getBasicBlockOrEnsureCurrentBlock(int index, int currentInstructionIndex) { | 
|  | // If the index is at current or past it ensure the block. | 
|  | if (index >= currentInstructionIndex) { | 
|  | ensureCurrentBlock(); | 
|  | return currentBlock; | 
|  | } | 
|  | // Otherwise we assume the index is an exact block index for an existing block. | 
|  | assert blocks.containsKey(index); | 
|  | return getBasicBlock(index); | 
|  | } | 
|  |  | 
|  | private void advanceInstructionState() { | 
|  | ensureCurrentBlock(); | 
|  | ensureCurrentPosition(); | 
|  | ++nextInstructionIndex; | 
|  | } | 
|  |  | 
|  | private void addInstruction(Instruction instruction) { | 
|  | int index = toInstructionIndexInIR(peekNextInstructionIndex()); | 
|  | advanceInstructionState(); | 
|  | instruction.setPosition(currentPosition); | 
|  | currentBlock.getInstructions().add(instruction); | 
|  | instruction.setBlock(currentBlock); | 
|  | int[] debugEndIndices = code.getDebugLocalEnds(index); | 
|  | if (debugEndIndices != null) { | 
|  | for (int encodedDebugEndIndex : debugEndIndices) { | 
|  | EV debugEndIndex = code.decodeValueIndex(encodedDebugEndIndex, index); | 
|  | Value debugValue = getValue(debugEndIndex); | 
|  | debugValue.addDebugLocalEnd(instruction); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void addThisArgument(DexType type) { | 
|  | boolean receiverCouldBeNull = buildForInlining; | 
|  | Nullability nullability = | 
|  | receiverCouldBeNull ? Nullability.maybeNull() : Nullability.definitelyNotNull(); | 
|  | TypeElement typeElement = type.toTypeElement(appView, nullability); | 
|  | Value dest = | 
|  | strategy.getValueDefinitionForInstructionIndex(0, typeElement, code::getDebugLocalInfo); | 
|  | Argument argument = internalAddArgument(dest, false); | 
|  | argument.outValue().markAsThis(); | 
|  | } | 
|  |  | 
|  | private void addNonThisArgument(DexType type, int index) { | 
|  | TypeElement typeElement = type.toTypeElement(appView); | 
|  | Value dest = | 
|  | strategy.getValueDefinitionForInstructionIndex( | 
|  | index, typeElement, code::getDebugLocalInfo); | 
|  | internalAddArgument(dest, type.isBooleanType()); | 
|  | } | 
|  |  | 
|  | private void addUnusedArgument(DexType type) { | 
|  | // Extra unused null arguments don't have valid indexes in LIR and must not adjust existing | 
|  | // indexes. | 
|  | TypeElement typeElement = | 
|  | type.isReferenceType() ? TypeElement.getNull() : type.toTypeElement(appView); | 
|  | Value dest = strategy.getFreshUnusedValue(typeElement); | 
|  | internalAddArgument(dest, false); | 
|  | } | 
|  |  | 
|  | private Argument internalAddArgument(Value dest, boolean isBooleanType) { | 
|  | assert currentBlock != null; | 
|  | // Arguments are not included in the "instructions" so this does not call "addInstruction" | 
|  | // which would otherwise advance the state. | 
|  | Argument argument = new Argument(dest, currentBlock.size(), isBooleanType); | 
|  | argument.setPosition(currentPosition); | 
|  | currentBlock.getInstructions().add(argument); | 
|  | argument.setBlock(currentBlock); | 
|  | return argument; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInstruction() { | 
|  | throw new Unimplemented("Missing IR conversion"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstNull() { | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getNull()); | 
|  | addInstruction(new ConstNumber(dest, 0)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstInt(int value) { | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getInt()); | 
|  | addInstruction(new ConstNumber(dest, value)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstFloat(int value) { | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getFloat()); | 
|  | addInstruction(new ConstNumber(dest, value)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstLong(long value) { | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getLong()); | 
|  | addInstruction(new ConstNumber(dest, value)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstDouble(long value) { | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getDouble()); | 
|  | addInstruction(new ConstNumber(dest, value)); | 
|  | } | 
|  |  | 
|  | TypeElement valueTypeElement(NumericType type) { | 
|  | return PrimitiveTypeElement.fromNumericType(type); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction( | 
|  | Add.createNonNormalized(type, dest, getValue(leftValueIndex), getValue(rightValueIndex))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(new Sub(type, dest, getValue(leftValueIndex), getValue(rightValueIndex))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onMul(NumericType type, EV leftValueIndex, EV rightValueIndex) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction( | 
|  | Mul.createNonNormalized(type, dest, getValue(leftValueIndex), getValue(rightValueIndex))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(new Div(type, dest, getValue(leftValueIndex), getValue(rightValueIndex))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onRem(NumericType type, EV leftValueIndex, EV rightValueIndex) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(new Rem(type, dest, getValue(leftValueIndex), getValue(rightValueIndex))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNeg(NumericType type, EV value) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(new Neg(type, dest, getValue(value))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNot(NumericType type, EV value) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(new Not(type, dest, getValue(value))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onShl(NumericType type, EV left, EV right) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(new Shl(type, dest, getValue(left), getValue(right))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onShr(NumericType type, EV left, EV right) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(new Shr(type, dest, getValue(left), getValue(right))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onUshr(NumericType type, EV left, EV right) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(new Ushr(type, dest, getValue(left), getValue(right))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onAnd(NumericType type, EV left, EV right) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(And.createNonNormalized(type, dest, getValue(left), getValue(right))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onOr(NumericType type, EV left, EV right) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(Or.createNonNormalized(type, dest, getValue(left), getValue(right))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onXor(NumericType type, EV left, EV right) { | 
|  | Value dest = getOutValueForNextInstruction(valueTypeElement(type)); | 
|  | addInstruction(Xor.createNonNormalized(type, dest, getValue(left), getValue(right))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstString(DexString string) { | 
|  | Value dest = | 
|  | getOutValueForNextInstruction( | 
|  | TypeElement.stringClassType(appView, Nullability.definitelyNotNull())); | 
|  | addInstruction(new ConstString(dest, string)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onDexItemBasedConstString( | 
|  | DexReference item, NameComputationInfo<?> nameComputationInfo) { | 
|  | Value dest = | 
|  | getOutValueForNextInstruction( | 
|  | TypeElement.stringClassType(appView, Nullability.definitelyNotNull())); | 
|  | addInstruction(new DexItemBasedConstString(dest, item, nameComputationInfo)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstClass(DexType type, boolean ignoreCompatRules) { | 
|  | Value dest = | 
|  | getOutValueForNextInstruction( | 
|  | TypeElement.classClassType(appView, Nullability.definitelyNotNull())); | 
|  | addInstruction(new ConstClass(dest, type, ignoreCompatRules)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstMethodHandle(DexMethodHandle methodHandle) { | 
|  | TypeElement handleType = | 
|  | TypeElement.fromDexType( | 
|  | appView.dexItemFactory().methodHandleType, Nullability.definitelyNotNull(), appView); | 
|  | Value dest = getOutValueForNextInstruction(handleType); | 
|  | addInstruction(new ConstMethodHandle(dest, methodHandle)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onConstMethodType(DexProto methodType) { | 
|  | TypeElement typeElement = | 
|  | TypeElement.fromDexType( | 
|  | appView.dexItemFactory().methodTypeType, Nullability.definitelyNotNull(), appView); | 
|  | Value dest = getOutValueForNextInstruction(typeElement); | 
|  | addInstruction(new ConstMethodType(dest, methodType)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNumberConversion(NumericType from, NumericType to, EV value) { | 
|  | Value dest = | 
|  | getOutValueForNextInstruction( | 
|  | to.toDexType(appView.dexItemFactory()).toTypeElement(appView)); | 
|  | addInstruction(new NumberConversion(from, to, dest, getValue(value))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onIf(IfType ifKind, int blockIndex, EV valueIndex) { | 
|  | BasicBlock targetBlock = getBasicBlock(blockIndex); | 
|  | Value value = getValue(valueIndex); | 
|  | addInstruction(new If(ifKind, value)); | 
|  | currentBlock.link(targetBlock); | 
|  | currentBlock.link(getBasicBlock(nextInstructionIndex)); | 
|  | closeCurrentBlock(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) { | 
|  | BasicBlock targetBlock = getBasicBlock(blockIndex); | 
|  | Value leftValue = getValue(leftValueIndex); | 
|  | Value rightValue = getValue(rightValueIndex); | 
|  | addInstruction(new If(ifKind, ImmutableList.of(leftValue, rightValue))); | 
|  | currentBlock.link(targetBlock); | 
|  | currentBlock.link(getBasicBlock(nextInstructionIndex)); | 
|  | closeCurrentBlock(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onFallthrough() { | 
|  | int nextBlockIndex = peekNextInstructionIndex() + 1; | 
|  | onGoto(nextBlockIndex); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onGoto(int blockIndex) { | 
|  | BasicBlock targetBlock = getBasicBlock(blockIndex); | 
|  | addInstruction(new Goto()); | 
|  | currentBlock.link(targetBlock); | 
|  | closeCurrentBlock(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onIntSwitch(EV value, IntSwitchPayload payload) { | 
|  | // keys is the 'value' -> 'target index' mapping. | 
|  | int[] keys = payload.keys; | 
|  | addSwitchInstruction( | 
|  | payload.targets, | 
|  | (successors, fallthrough) -> | 
|  | new IntSwitch(getValue(value), keys, successors, fallthrough)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onStringSwitch(EV value, StringSwitchPayload payload) { | 
|  | int size = payload.keys.length; | 
|  | DexString[] keys = new DexString[size]; | 
|  | for (int i = 0; i < size; i++) { | 
|  | keys[i] = (DexString) code.getConstantItem(payload.keys[i]); | 
|  | } | 
|  | addSwitchInstruction( | 
|  | payload.targets, | 
|  | (successors, fallthrough) -> | 
|  | new StringSwitch(getValue(value), keys, successors, fallthrough)); | 
|  | } | 
|  |  | 
|  | private void addSwitchInstruction( | 
|  | int[] targets, BiFunction<int[], Integer, Instruction> createSwitchInstruction) { | 
|  | int size = targets.length; | 
|  | // successorIndices is the 'target index' to 'IR successor index'. | 
|  | int[] successorIndices = new int[size]; | 
|  | List<BasicBlock> successorBlocks = new ArrayList<>(size); | 
|  | // The mapping from instruction to successor is a temp mapping to track if any targets | 
|  | // point to the same block. | 
|  | Int2IntMap instructionToSuccessor = new Int2IntOpenHashMap(size); | 
|  | for (int i = 0; i < targets.length; i++) { | 
|  | int instructionIndex = targets[i]; | 
|  | if (instructionToSuccessor.containsKey(instructionIndex)) { | 
|  | successorIndices[i] = instructionToSuccessor.get(instructionIndex); | 
|  | } else { | 
|  | int successorIndex = successorBlocks.size(); | 
|  | successorIndices[i] = successorIndex; | 
|  | instructionToSuccessor.put(instructionIndex, successorIndex); | 
|  | successorBlocks.add(getBasicBlock(instructionIndex)); | 
|  | } | 
|  | } | 
|  | int fallthrough = successorBlocks.size(); | 
|  | addInstruction(createSwitchInstruction.apply(successorIndices, fallthrough)); | 
|  | // The call to addInstruction will ensure the current block so don't amend to it before here. | 
|  | // If the block has successors then the index mappings are not valid / need to be offset. | 
|  | assert currentBlock.getSuccessors().isEmpty(); | 
|  | successorBlocks.forEach(currentBlock::link); | 
|  | currentBlock.link(getBasicBlock(nextInstructionIndex)); | 
|  | closeCurrentBlock(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInvokeDirect(DexMethod target, List<EV> arguments, boolean isInterface) { | 
|  | Value dest = getInvokeInstructionOutputValue(target); | 
|  | List<Value> ssaArgumentValues = getValues(arguments); | 
|  | InvokeDirect instruction = new InvokeDirect(target, dest, ssaArgumentValues, isInterface); | 
|  | addInstruction(instruction); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInvokeSuper(DexMethod method, List<EV> arguments, boolean isInterface) { | 
|  | Value dest = getInvokeInstructionOutputValue(method); | 
|  | List<Value> ssaArgumentValues = getValues(arguments); | 
|  | InvokeSuper instruction = new InvokeSuper(method, dest, ssaArgumentValues, isInterface); | 
|  | addInstruction(instruction); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInvokeVirtual(DexMethod target, List<EV> arguments) { | 
|  | Value dest = getInvokeInstructionOutputValue(target); | 
|  | List<Value> ssaArgumentValues = getValues(arguments); | 
|  | InvokeVirtual instruction = new InvokeVirtual(target, dest, ssaArgumentValues); | 
|  | addInstruction(instruction); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInvokeStatic(DexMethod target, List<EV> arguments, boolean isInterface) { | 
|  | Value dest = getInvokeInstructionOutputValue(target); | 
|  | List<Value> ssaArgumentValues = getValues(arguments); | 
|  | InvokeStatic instruction = new InvokeStatic(target, dest, ssaArgumentValues, isInterface); | 
|  | addInstruction(instruction); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInvokeInterface(DexMethod target, List<EV> arguments) { | 
|  | Value dest = getInvokeInstructionOutputValue(target); | 
|  | List<Value> ssaArgumentValues = getValues(arguments); | 
|  | InvokeInterface instruction = new InvokeInterface(target, dest, ssaArgumentValues); | 
|  | addInstruction(instruction); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInvokeCustom(DexCallSite callSite, List<EV> arguments) { | 
|  | // The actual type of invoke custom may have multiple interface types. Defer type to widening. | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getBottom()); | 
|  | List<Value> ssaArgumentValues = getValues(arguments); | 
|  | InvokeCustom instruction = new InvokeCustom(callSite, dest, ssaArgumentValues); | 
|  | addInstruction(instruction); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInvokePolymorphic(DexMethod target, DexProto proto, List<EV> arguments) { | 
|  | Value dest = getInvokeInstructionOutputValue(proto); | 
|  | List<Value> ssaArgumentValues = getValues(arguments); | 
|  | InvokePolymorphic instruction = new InvokePolymorphic(target, proto, dest, ssaArgumentValues); | 
|  | addInstruction(instruction); | 
|  | } | 
|  |  | 
|  | private Value getInvokeInstructionOutputValue(DexMethod target) { | 
|  | return getInvokeInstructionOutputValue(target.getProto()); | 
|  | } | 
|  |  | 
|  | private Value getInvokeInstructionOutputValue(DexProto target) { | 
|  | DexType returnType = target.getReturnType(); | 
|  | return returnType.isVoidType() | 
|  | ? null | 
|  | : getOutValueForNextInstruction(returnType.toTypeElement(appView)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNewInstance(DexType clazz) { | 
|  | TypeElement type = TypeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appView); | 
|  | Value dest = getOutValueForNextInstruction(type); | 
|  | addInstruction(new NewInstance(clazz, dest)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onStaticGet(DexField field) { | 
|  | Value dest = getOutValueForNextInstruction(field.getTypeElement(appView)); | 
|  | addInstruction(new StaticGet(dest, field)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onStaticPut(DexField field, EV value) { | 
|  | addInstruction(new StaticPut(getValue(value), field)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInstanceGet(DexField field, EV object) { | 
|  | Value dest = getOutValueForNextInstruction(field.getTypeElement(appView)); | 
|  | addInstruction(new InstanceGet(dest, getValue(object), field)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInstancePut(DexField field, EV object, EV value) { | 
|  | addInstruction( | 
|  | InstancePut.createPotentiallyInvalid(field, getValue(object), getValue(value))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNewArrayEmpty(DexType type, EV size) { | 
|  | Value dest = | 
|  | getOutValueForNextInstruction( | 
|  | type.toTypeElement(appView, Nullability.definitelyNotNull())); | 
|  | addInstruction(new NewArrayEmpty(dest, getValue(size), type)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onThrow(EV exception) { | 
|  | addInstruction(new Throw(getValue(exception))); | 
|  | closeCurrentBlock(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onReturnVoid() { | 
|  | addInstruction(new Return()); | 
|  | closeCurrentBlock(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onReturn(EV value) { | 
|  | if (protoChanges.hasBeenChangedToReturnVoid()) { | 
|  | onReturnVoid(); | 
|  | } else { | 
|  | addInstruction(new Return(getValue(value))); | 
|  | closeCurrentBlock(); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onArrayLength(EV arrayValueIndex) { | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getInt()); | 
|  | Value arrayValue = getValue(arrayValueIndex); | 
|  | addInstruction(new ArrayLength(dest, arrayValue)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onCheckCast(DexType type, EV value, boolean ignoreCompatRules) { | 
|  | Value dest = getOutValueForNextInstruction(type.toTypeElement(appView, Nullability.bottom())); | 
|  | addInstruction(new CheckCast(dest, getValue(value), type, ignoreCompatRules)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onSafeCheckCast(DexType type, EV value) { | 
|  | Value dest = getOutValueForNextInstruction(type.toTypeElement(appView, Nullability.bottom())); | 
|  | addInstruction(new SafeCheckCast(dest, getValue(value), type)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInstanceOf(DexType type, EV value) { | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getInt()); | 
|  | addInstruction(new InstanceOf(dest, getValue(value), type)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onDebugPosition() { | 
|  | addInstruction(new DebugPosition()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onPhi(List<EV> operands) { | 
|  | // The type of the phi is determined by its operands during type widening. | 
|  | Phi phi = getPhiForNextInstructionAndAdvanceState(TypeElement.getBottom()); | 
|  | List<Value> values = new ArrayList<>(operands.size()); | 
|  | for (int i = 0; i < operands.size(); i++) { | 
|  | values.add(getValue(operands.get(i))); | 
|  | } | 
|  | phi.addOperands(values, false); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onMoveException(DexType exceptionType) { | 
|  | Value dest = | 
|  | getOutValueForNextInstruction( | 
|  | exceptionType.toTypeElement(appView, Nullability.definitelyNotNull())); | 
|  | addInstruction(new MoveException(dest, exceptionType, appView.options())); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onDebugLocalWrite(EV srcIndex) { | 
|  | // The type is dependent on the source so type widening will determine it. | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getBottom()); | 
|  | addInstruction(new DebugLocalWrite(dest, getValue(srcIndex))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onDebugLocalRead() { | 
|  | addInstruction(new DebugLocalRead()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInvokeMultiNewArray(DexType type, List<EV> arguments) { | 
|  | Value dest = | 
|  | getOutValueForNextInstruction( | 
|  | type.toTypeElement(appView, Nullability.definitelyNotNull())); | 
|  | addInstruction(new InvokeMultiNewArray(type, dest, getValues(arguments))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNewArrayFilled(DexType type, List<EV> arguments) { | 
|  | Value dest = | 
|  | getOutValueForNextInstruction( | 
|  | type.toTypeElement(appView, Nullability.definitelyNotNull())); | 
|  | addInstruction(new NewArrayFilled(type, dest, getValues(arguments))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNewArrayFilledData(int elementWidth, long size, short[] data, EV src) { | 
|  | addInstruction(new NewArrayFilledData(getValue(src), elementWidth, size, data)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onCmpInstruction(int opcode, EV leftIndex, EV rightIndex) { | 
|  | NumericType type; | 
|  | Bias bias; | 
|  | switch (opcode) { | 
|  | case LirOpcodes.LCMP: | 
|  | type = NumericType.LONG; | 
|  | bias = Bias.NONE; | 
|  | break; | 
|  | case LirOpcodes.FCMPL: | 
|  | type = NumericType.FLOAT; | 
|  | bias = Bias.LT; | 
|  | break; | 
|  | case LirOpcodes.FCMPG: | 
|  | type = NumericType.FLOAT; | 
|  | bias = Bias.GT; | 
|  | break; | 
|  | case LirOpcodes.DCMPL: | 
|  | type = NumericType.DOUBLE; | 
|  | bias = Bias.LT; | 
|  | break; | 
|  | case LirOpcodes.DCMPG: | 
|  | type = NumericType.DOUBLE; | 
|  | bias = Bias.GT; | 
|  | break; | 
|  | default: | 
|  | throw new Unreachable("Unexpected cmp opcode: " + opcode); | 
|  | } | 
|  | Value leftValue = getValue(leftIndex); | 
|  | Value rightValue = getValue(rightIndex); | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getInt()); | 
|  | addInstruction(new Cmp(type, bias, dest, leftValue, rightValue)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onMonitorEnter(EV value) { | 
|  | addInstruction(new Monitor(MonitorType.ENTER, getValue(value))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onMonitorExit(EV value) { | 
|  | addInstruction(new Monitor(MonitorType.EXIT, getValue(value))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onArrayGet(MemberType type, EV array, EV index) { | 
|  | TypeElement typeElement; | 
|  | if (type.isObject()) { | 
|  | // The actual object type must be computed from its array value. | 
|  | typeElement = TypeElement.getBottom(); | 
|  | } else { | 
|  | // Convert the member type to a "stack value type", e.g., byte, char etc to int. | 
|  | ValueType valueType = ValueType.fromMemberType(type); | 
|  | DexType dexType = valueType.toDexType(appView.dexItemFactory()); | 
|  | typeElement = dexType.toTypeElement(appView); | 
|  | } | 
|  | Value dest = getOutValueForNextInstruction(typeElement); | 
|  | addInstruction(new ArrayGet(type, dest, getValue(array), getValue(index))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onArrayPut(MemberType type, EV array, EV index, EV value) { | 
|  | addInstruction( | 
|  | ArrayPut.createWithoutVerification( | 
|  | type, getValue(array), getValue(index), getValue(value))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNewUnboxedEnumInstance(DexType clazz, int ordinal) { | 
|  | TypeElement type = TypeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appView); | 
|  | Value dest = getOutValueForNextInstruction(type); | 
|  | addInstruction(new NewUnboxedEnumInstance(clazz, ordinal, dest)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onInitClass(DexType clazz) { | 
|  | Value dest = getOutValueForNextInstruction(TypeElement.getInt()); | 
|  | addInstruction(new InitClass(dest, clazz)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onRecordFieldValues(DexField[] fields, List<EV> values) { | 
|  | TypeElement typeElement = | 
|  | TypeElement.fromDexType( | 
|  | appView.dexItemFactory().objectArrayType, Nullability.definitelyNotNull(), appView); | 
|  | Value dest = getOutValueForNextInstruction(typeElement); | 
|  | addInstruction(new RecordFieldValues(fields, dest, getValues(values))); | 
|  | } | 
|  | } | 
|  | } |