blob: ae06b60883ea48dd753ff9acbffea9b93f1da305 [file] [log] [blame]
// 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;
}
}