blob: 389f9d9439c6ce4804df123f54ce0f89e61f774b [file] [log] [blame]
// Copyright (c) 2023, 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.naming;
import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeCustomOnRecord;
import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.parseInvokeCustomOnRecord;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
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.DexItemFactory;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
import com.android.tools.r8.ir.desugar.records.RecordRewriter;
import com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.RecordInvokeDynamic;
import java.util.ArrayList;
/** Rewrites the record invokedynamic/invoke-custom in hashCode, equals and toString. */
public class RecordInvokeDynamicInvokeCustomRewriter
extends CodeRewriterPass<AppInfoWithClassHierarchy> {
private final RecordRewriter recordRewriter;
public RecordInvokeDynamicInvokeCustomRewriter(
AppView<? extends AppInfoWithClassHierarchy> appView) {
super(appView);
this.recordRewriter = RecordRewriter.create(appView);
}
@Override
protected String getRewriterId() {
return "RecordInvokeDynamicInvokeCustomRewriter";
}
@Override
protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
return recordRewriter != null
&& code.context().getHolder().isRecord()
&& code.metadata().mayHaveInvokeCustom();
}
@Override
protected CodeRewriterResult rewriteCode(IRCode code) {
boolean hasChanged = false;
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
InvokeCustom instruction = iterator.next().asInvokeCustom();
if (instruction != null) {
InvokeCustom replacement = rewriteRecordInvokeCustom(code, instruction);
if (replacement != instruction) {
iterator.replaceCurrentInstruction(replacement);
hasChanged = true;
}
}
}
return CodeRewriterResult.hasChanged(hasChanged);
}
// Called after final tree shaking, prune and minify field names and field values.
public InvokeCustom rewriteRecordInvokeCustom(IRCode code, InvokeCustom invokeCustom) {
ProgramMethod context = code.context();
if (!isInvokeCustomOnRecord(invokeCustom, appView, context)) {
return invokeCustom;
}
RecordInvokeDynamic recordInvokeDynamic =
parseInvokeCustomOnRecord(invokeCustom, appView, context);
DexString newFieldNames =
recordInvokeDynamic
.computeRecordFieldNamesComputationInfo()
.internalComputeNameFor(
recordInvokeDynamic.getRecordType(),
appView,
appView.getNamingLens());
DexField[] newFields =
recordRewriter.computePresentFields(appView.graphLens(), recordInvokeDynamic);
return writeRecordInvokeCustom(
invokeCustom, recordInvokeDynamic.withFieldNamesAndFields(newFieldNames, newFields));
}
private InvokeCustom writeRecordInvokeCustom(
InvokeCustom invokeCustom, RecordInvokeDynamic recordInvokeDynamic) {
DexItemFactory factory = appView.dexItemFactory();
DexMethodHandle bootstrapMethod =
factory.createMethodHandle(
MethodHandleType.INVOKE_STATIC, factory.objectMethodsMembers.bootstrap, false, null);
ArrayList<DexValue> bootstrapArgs = new ArrayList<>();
bootstrapArgs.add(new DexValueType(recordInvokeDynamic.getRecordCodeType()));
bootstrapArgs.add(new DexValueString(recordInvokeDynamic.getFieldNames()));
for (DexField field : recordInvokeDynamic.getFields()) {
// Rewrite using the code type of the field.
DexField codeField =
factory.createField(recordInvokeDynamic.getRecordCodeType(), field.type, field.name);
bootstrapArgs.add(
new DexValueMethodHandle(
factory.createMethodHandle(MethodHandleType.INSTANCE_GET, codeField, false, null)));
}
DexCallSite callSite =
factory.createCallSite(
recordInvokeDynamic.getMethodName(),
recordInvokeDynamic.getMethodProto(),
bootstrapMethod,
bootstrapArgs);
DexType returnType = callSite.getMethodProto().getReturnType();
assert returnType.isBooleanType()
|| returnType.isIntType()
|| returnType.isIdenticalTo(factory.stringType);
assert invokeCustom.outValue().getType().equals(returnType.toTypeElement(appView));
return new InvokeCustom(callSite, invokeCustom.outValue(), invokeCustom.arguments());
}
}