diff options
author | Stephan Herhut <herhut@google.com> | 2017-05-30 14:33:11 +0200 |
---|---|---|
committer | Stephan Herhut <herhut@google.com> | 2017-05-30 14:33:11 +0200 |
commit | 080ec2d61f7b055a55802c072e77e99514499b8f (patch) | |
tree | 28386639b10843613c493ff7a5501371539f4c04 /src/main/java/com/android/tools/r8/ir/synthetic | |
parent | 35b1cb677b6db60e48c4aec18b5649082152afd9 (diff) | |
download | r8-080ec2d61f7b055a55802c072e77e99514499b8f.tar.gz |
Reland "Insert bridge methods in member rebinding if target is otherwise not visible."
Bug:
Change-Id: Icdf351384330ca4781d1dfbd510f1be68c0874f4
Diffstat (limited to 'src/main/java/com/android/tools/r8/ir/synthetic')
3 files changed, 385 insertions, 0 deletions
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java new file mode 100644 index 000000000..bcc9462be --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java @@ -0,0 +1,105 @@ +// 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.Unimplemented; +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.Invoke; +import com.android.tools.r8.ir.code.MoveType; +import com.android.tools.r8.ir.conversion.IRBuilder; +import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.List; + +// Source code representing simple forwarding method. +public final class ForwardMethodSourceCode extends SingleBlockSourceCode { + + private final DexType targetReceiver; + private final DexMethod target; + private final Invoke.Type invokeType; + + public ForwardMethodSourceCode(DexType receiver, DexProto proto, + DexType targetReceiver, DexMethod target, Invoke.Type invokeType) { + super(receiver, proto); + assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC); + + this.target = target; + this.targetReceiver = targetReceiver; + this.invokeType = invokeType; + assert checkSignatures(); + + switch (invokeType) { + case STATIC: + case SUPER: + case INTERFACE: + case VIRTUAL: + break; + default: + throw new Unimplemented("Invoke type " + invokeType + " is not yet supported."); + } + } + + private boolean checkSignatures() { + List<DexType> sourceParams = new ArrayList<>(); + if (receiver != null) { + sourceParams.add(receiver); + } + sourceParams.addAll(Lists.newArrayList(proto.parameters.values)); + + List<DexType> targetParams = new ArrayList<>(); + if (targetReceiver != null) { + targetParams.add(targetReceiver); + } + targetParams.addAll(Lists.newArrayList(target.proto.parameters.values)); + + assert sourceParams.size() == targetParams.size(); + for (int i = 0; i < sourceParams.size(); i++) { + DexType source = sourceParams.get(i); + DexType target = targetParams.get(i); + + // We assume source is compatible with target if they both are classes. + // This check takes care of receiver widening conversion but does not + // many others, like conversion from an array to Object. + assert (source.isClassType() && target.isClassType()) || source == target; + } + + assert this.proto.returnType == target.proto.returnType; + return true; + } + + @Override + protected void prepareInstructions() { + // Prepare call arguments. + List<MoveType> argMoveTypes = new ArrayList<>(); + List<Integer> argRegisters = new ArrayList<>(); + + if (receiver != null) { + argMoveTypes.add(MoveType.OBJECT); + argRegisters.add(getReceiverRegister()); + } + + DexType[] accessorParams = proto.parameters.values; + for (int i = 0; i < accessorParams.length; i++) { + argMoveTypes.add(MoveType.fromDexType(accessorParams[i])); + argRegisters.add(getParamRegister(i)); + } + + // Method call to the target method. + add(builder -> builder.addInvoke(this.invokeType, + this.target, this.target.proto, argMoveTypes, argRegisters)); + + // Does the method return value? + if (proto.returnType.isVoidType()) { + add(IRBuilder::addReturn); + } else { + MoveType moveType = MoveType.fromDexType(proto.returnType); + int tempValue = nextRegister(moveType); + add(builder -> builder.addMoveResult(moveType, tempValue)); + add(builder -> builder.addReturn(moveType, tempValue)); + } + } +} diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java new file mode 100644 index 000000000..8c97f8ff1 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java @@ -0,0 +1,213 @@ +// 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.DexProto; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.code.Argument; +import com.android.tools.r8.ir.code.CatchHandlers; +import com.android.tools.r8.ir.code.MoveType; +import com.android.tools.r8.ir.code.Switch; +import com.android.tools.r8.ir.code.Value; +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; + +public abstract class SingleBlockSourceCode implements SourceCode { + + protected final DexType receiver; + 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<>(); + + protected SingleBlockSourceCode(DexType receiver, DexProto proto) { + assert proto != null; + this.receiver = receiver; + this.proto = proto; + + // Initialize register values for receiver and arguments + this.receiverRegister = receiver != null ? nextRegister(MoveType.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(MoveType.fromDexType(params[i])); + } + } + + protected final void add(Consumer<IRBuilder> constructor) { + constructors.add(constructor); + } + + protected final int nextRegister(MoveType type) { + int value = nextRegister; + nextRegister += type == MoveType.WIDE ? 2 : 1; + 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 boolean needsPrelude() { + return receiver != null || paramRegisters.length > 0; + } + + @Override + public final int instructionCount() { + return constructors.size(); + } + + @Override + public final int instructionIndex(int instructionOffset) { + return instructionOffset; + } + + @Override + public final int instructionOffset(int instructionIndex) { + return instructionIndex; + } + + @Override + public DebugLocalInfo getCurrentLocal(int register) { + return null; + } + + @Override + public final boolean traceInstruction(int instructionIndex, IRBuilder builder) { + return instructionIndex == constructors.size() - 1; + } + + @Override + public final void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) { + } + + @Override + public final void closedCurrentBlock() { + } + + @Override + public final void setUp() { + assert constructors.isEmpty(); + prepareInstructions(); + assert !constructors.isEmpty(); + } + + @Override + public final void clear() { + constructors = null; + paramRegisters = null; + paramValues = null; + receiverValue = null; + } + + @Override + public final void buildPrelude(IRBuilder builder) { + if (receiver != null) { + receiverValue = builder.writeRegister(receiverRegister, MoveType.OBJECT, NO_THROW); + builder.add(new Argument(receiverValue)); + } + + // Fill in the Argument instructions in the argument block. + DexType[] parameters = proto.parameters.values; + for (int i = 0; i < parameters.length; i++) { + MoveType moveType = MoveType.fromDexType(parameters[i]); + Value paramValue = builder.writeRegister(paramRegisters[i], moveType, 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) { + constructors.get(instructionIndex).accept(builder); + } + + @Override + public final void resolveAndBuildSwitch( + Switch.Type type, 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 final boolean verifyCurrentInstructionCanThrow() { + return true; + } + + @Override + public boolean verifyLocalInScope(DebugLocalInfo local) { + return true; + } + + @Override + public final boolean verifyRegister(int register) { + return true; + } +} diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java new file mode 100644 index 000000000..7716663f7 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java @@ -0,0 +1,67 @@ +// 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.Code; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.ir.conversion.SourceCode; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.InternalOptions; +import java.util.function.Consumer; + +public final class SynthesizedCode extends Code { + + private final SourceCode sourceCode; + private final Consumer<UseRegistry> registryCallback; + + public SynthesizedCode(SourceCode sourceCode) { + this.sourceCode = sourceCode; + this.registryCallback = SynthesizedCode::registerReachableDefinitionsDefault; + } + + public SynthesizedCode(SourceCode sourceCode, Consumer<UseRegistry> callback) { + this.sourceCode = sourceCode; + this.registryCallback = callback; + } + + @Override + public final IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) { + return new IRBuilder(encodedMethod, sourceCode, options).build(); + } + + @Override + public final String toString() { + return toString(null); + } + + private static void registerReachableDefinitionsDefault(UseRegistry registry) { + throw new Unreachable(); + } + + @Override + public void registerReachableDefinitions(UseRegistry registry) { + registryCallback.accept(registry); + } + + @Override + protected final int computeHashCode() { + return sourceCode.hashCode(); + } + + @Override + protected final boolean computeEquals(Object other) { + return other instanceof SynthesizedCode && + this.sourceCode.equals(((SynthesizedCode) other).sourceCode); + } + + @Override + public final String toString(ClassNameMapper naming) { + return "SynthesizedCode: " + sourceCode.toString(); + } +} |