| // 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 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.code.CatchHandlers; |
| import com.android.tools.r8.ir.code.Position; |
| import com.android.tools.r8.ir.code.ValueType; |
| import com.android.tools.r8.ir.conversion.DexSourceCode; |
| 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; |
| |
| // TODO(b/146124603): Remove these fields as optimizations (e.g., merging) could invalidate them. |
| 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; |
| |
| // 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]; |
| 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 int getReceiverRegister() { |
| assert receiver != null; |
| assert receiverRegister >= 0; |
| return receiverRegister; |
| } |
| |
| 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; |
| } |
| |
| @Override |
| public final void buildPrelude(IRBuilder builder) { |
| builder.buildArgumentsWithRewrittenPrototypeChanges( |
| 0, builder.getMethod(), DexSourceCode::doNothingWriteConsumer); |
| } |
| |
| @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) { |
| // Intentionally 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(IRBuilder builder) { |
| 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; |
| } |
| } |