|  | // Copyright (c) 2017, 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.ir.synthetic; | 
|  |  | 
|  | import static com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo.NO_THROW; | 
|  |  | 
|  | import com.android.tools.r8.errors.Unreachable; | 
|  | import com.android.tools.r8.graph.DebugLocalInfo; | 
|  | import com.android.tools.r8.graph.DexMethod; | 
|  | import com.android.tools.r8.graph.DexProto; | 
|  | import com.android.tools.r8.graph.DexType; | 
|  | import com.android.tools.r8.ir.analysis.type.TypeLatticeElement; | 
|  | import com.android.tools.r8.ir.code.Argument; | 
|  | import com.android.tools.r8.ir.code.CatchHandlers; | 
|  | import com.android.tools.r8.ir.code.Position; | 
|  | import com.android.tools.r8.ir.code.Value; | 
|  | import com.android.tools.r8.ir.code.ValueType; | 
|  | import com.android.tools.r8.ir.conversion.IRBuilder; | 
|  | import com.android.tools.r8.ir.conversion.SourceCode; | 
|  | import java.util.ArrayList; | 
|  | import java.util.List; | 
|  | import java.util.function.Consumer; | 
|  | import java.util.function.Predicate; | 
|  |  | 
|  | public abstract class SyntheticSourceCode implements SourceCode { | 
|  | protected final static Predicate<IRBuilder> doesNotEndBlock = x -> false; | 
|  | protected final static Predicate<IRBuilder> endsBlock = x -> true; | 
|  |  | 
|  | protected final DexType receiver; | 
|  | protected final DexMethod method; | 
|  | protected final DexProto proto; | 
|  |  | 
|  | // The next free register, note that we always | 
|  | // assign each value a new (next available) register. | 
|  | private int nextRegister = 0; | 
|  |  | 
|  | // Registers for receiver and parameters | 
|  | private final int receiverRegister; | 
|  | private int[] paramRegisters; | 
|  | // Values representing receiver and parameters will be filled in | 
|  | // buildPrelude() and should only be accessed via appropriate methods | 
|  | private Value receiverValue; | 
|  | private Value[] paramValues; | 
|  |  | 
|  | // Instruction constructors | 
|  | private List<Consumer<IRBuilder>> constructors = new ArrayList<>(); | 
|  | private List<Predicate<IRBuilder>> traceEvents = new ArrayList<>(); | 
|  |  | 
|  | private final Position position; | 
|  |  | 
|  | protected SyntheticSourceCode(DexType receiver, DexMethod method, Position callerPosition) { | 
|  | this(receiver, method, callerPosition, method); | 
|  | } | 
|  |  | 
|  | protected SyntheticSourceCode( | 
|  | DexType receiver, DexMethod method, Position callerPosition, DexMethod originalMethod) { | 
|  | assert method != null; | 
|  | this.receiver = receiver; | 
|  | this.method = method; | 
|  | this.proto = method.proto; | 
|  |  | 
|  | // Initialize register values for receiver and arguments | 
|  | this.receiverRegister = receiver != null ? nextRegister(ValueType.OBJECT) : -1; | 
|  |  | 
|  | DexType[] params = proto.parameters.values; | 
|  | int paramCount = params.length; | 
|  | this.paramRegisters = new int[paramCount]; | 
|  | this.paramValues = new Value[paramCount]; | 
|  | for (int i = 0; i < paramCount; i++) { | 
|  | this.paramRegisters[i] = nextRegister(ValueType.fromDexType(params[i])); | 
|  | } | 
|  |  | 
|  | position = Position.synthetic(0, originalMethod, callerPosition); | 
|  | } | 
|  |  | 
|  | protected final void add(Consumer<IRBuilder> constructor) { | 
|  | add(constructor, doesNotEndBlock); | 
|  | } | 
|  |  | 
|  | protected final void add(Consumer<IRBuilder> constructor, Predicate<IRBuilder> traceEvent) { | 
|  | constructors.add(constructor); | 
|  | traceEvents.add(traceEvent); | 
|  | } | 
|  |  | 
|  | protected final int nextRegister(ValueType type) { | 
|  | int value = nextRegister; | 
|  | nextRegister += type.requiredRegisters(); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | protected final Value getReceiverValue() { | 
|  | assert receiver != null; | 
|  | assert receiverValue != null; | 
|  | return receiverValue; | 
|  | } | 
|  |  | 
|  | protected final int getReceiverRegister() { | 
|  | assert receiver != null; | 
|  | assert receiverRegister >= 0; | 
|  | return receiverRegister; | 
|  | } | 
|  |  | 
|  | protected final Value getParamValue(int paramIndex) { | 
|  | assert paramIndex >= 0; | 
|  | assert paramIndex < paramValues.length; | 
|  | return paramValues[paramIndex]; | 
|  | } | 
|  |  | 
|  | protected final int getParamCount() { | 
|  | return paramValues.length; | 
|  | } | 
|  |  | 
|  | protected final int getParamRegister(int paramIndex) { | 
|  | assert paramIndex >= 0; | 
|  | assert paramIndex < paramRegisters.length; | 
|  | return paramRegisters[paramIndex]; | 
|  | } | 
|  |  | 
|  | protected abstract void prepareInstructions(); | 
|  |  | 
|  | @Override | 
|  | public final int instructionCount() { | 
|  | return constructors.size(); | 
|  | } | 
|  |  | 
|  | protected final int lastInstructionIndex() { | 
|  | return constructors.size() - 1; | 
|  | } | 
|  |  | 
|  | protected final int nextInstructionIndex() { | 
|  | return constructors.size(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final int instructionIndex(int instructionOffset) { | 
|  | return instructionOffset; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final int instructionOffset(int instructionIndex) { | 
|  | return instructionIndex; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DebugLocalInfo getIncomingLocalAtBlock(int register, int blockOffset) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DebugLocalInfo getIncomingLocal(int register) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DebugLocalInfo getOutgoingLocal(int register) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final int traceInstruction(int instructionIndex, IRBuilder builder) { | 
|  | return (traceEvents.get(instructionIndex).test(builder) || | 
|  | (instructionIndex == constructors.size() - 1)) ? instructionIndex : -1; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final void setUp() { | 
|  | assert constructors.isEmpty(); | 
|  | prepareInstructions(); | 
|  | assert !constructors.isEmpty(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final void clear() { | 
|  | constructors = null; | 
|  | traceEvents = null; | 
|  | paramRegisters = null; | 
|  | paramValues = null; | 
|  | receiverValue = null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final void buildPrelude(IRBuilder builder) { | 
|  | if (receiver != null) { | 
|  | // TODO(zerny): Why does this not call builder.addThisArgument? | 
|  | receiverValue = | 
|  | builder.writeRegister( | 
|  | receiverRegister, | 
|  | TypeLatticeElement.fromDexType(receiver, false, builder.getAppInfo()), | 
|  | NO_THROW); | 
|  | builder.add(new Argument(receiverValue)); | 
|  | receiverValue.markAsThis(false); | 
|  | } | 
|  |  | 
|  | // Fill in the Argument instructions in the argument block. | 
|  | DexType[] parameters = proto.parameters.values; | 
|  | for (int i = 0; i < parameters.length; i++) { | 
|  | // TODO(zerny): Why does this not call builder.addNonThisArgument? | 
|  | TypeLatticeElement typeLattice = | 
|  | TypeLatticeElement.fromDexType(parameters[i], true, builder.getAppInfo()); | 
|  | Value paramValue = builder.writeRegister(paramRegisters[i], typeLattice, NO_THROW); | 
|  | paramValues[i] = paramValue; | 
|  | builder.add(new Argument(paramValue)); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final void buildPostlude(IRBuilder builder) { | 
|  | // Intentionally left empty. | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final void buildInstruction( | 
|  | IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) { | 
|  | constructors.get(instructionIndex).accept(builder); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void buildBlockTransfer( | 
|  | IRBuilder builder, int predecessorOffset, int successorOffset, boolean isExceptional) { | 
|  | // Intensionally empty as synthetic code does not contain locals information. | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final void resolveAndBuildSwitch( | 
|  | int value, int fallthroughOffset, int payloadOffset, IRBuilder builder) { | 
|  | throw new Unreachable("Unexpected call to resolveAndBuildSwitch"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final void resolveAndBuildNewArrayFilledData( | 
|  | int arrayRef, int payloadOffset, IRBuilder builder) { | 
|  | throw new Unreachable("Unexpected call to resolveAndBuildNewArrayFilledData"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final CatchHandlers<Integer> getCurrentCatchHandlers() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int getMoveExceptionRegister(int instructionIndex) { | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Position getCanonicalDebugPositionAtOffset(int offset) { | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Position getCurrentPosition() { | 
|  | return position; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final boolean verifyCurrentInstructionCanThrow() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean verifyLocalInScope(DebugLocalInfo local) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final boolean verifyRegister(int register) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // To be used as a tracing event for switch instruction., | 
|  | protected boolean endsSwitch( | 
|  | IRBuilder builder, int switchIndex, int fallthrough, int[] offsets) { | 
|  | // ensure successors of switch instruction | 
|  | for (int offset : offsets) { | 
|  | builder.ensureNormalSuccessorBlock(switchIndex, offset); | 
|  | } | 
|  | builder.ensureNormalSuccessorBlock(switchIndex, fallthrough); | 
|  | return true; | 
|  | } | 
|  | } |