|  | // Copyright (c) 2021, 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.horizontalclassmerging; | 
|  |  | 
|  | import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.ASSUME; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.DEX_ITEM_BASED_CONST_STRING; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.GOTO; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT; | 
|  | import static com.android.tools.r8.ir.code.Opcodes.RETURN; | 
|  |  | 
|  | import com.android.tools.r8.graph.AppInfoWithClassHierarchy; | 
|  | import com.android.tools.r8.graph.AppView; | 
|  | import com.android.tools.r8.graph.DexField; | 
|  | import com.android.tools.r8.graph.DexMethod; | 
|  | import com.android.tools.r8.graph.ProgramField; | 
|  | import com.android.tools.r8.graph.ProgramMethod; | 
|  | import com.android.tools.r8.ir.code.BasicBlock; | 
|  | import com.android.tools.r8.ir.code.IRCode; | 
|  | import com.android.tools.r8.ir.code.InstancePut; | 
|  | import com.android.tools.r8.ir.code.Instruction; | 
|  | import com.android.tools.r8.ir.code.InvokeDirect; | 
|  | import com.android.tools.r8.ir.code.Value; | 
|  | import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo; | 
|  | import com.android.tools.r8.utils.WorkList; | 
|  | import com.google.common.collect.Iterables; | 
|  | import java.util.ArrayList; | 
|  | import java.util.List; | 
|  |  | 
|  | public class InstanceInitializerAnalysis { | 
|  |  | 
|  | public static InstanceInitializerDescription analyze( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, | 
|  | IRCodeProvider codeProvider, | 
|  | MergeGroup group, | 
|  | ProgramMethod instanceInitializer) { | 
|  | InstanceInitializerDescription.Builder builder = | 
|  | InstanceInitializerDescription.builder(appView, instanceInitializer); | 
|  | IRCode code = codeProvider.buildIR(instanceInitializer); | 
|  | WorkList<BasicBlock> workList = WorkList.newIdentityWorkList(code.entryBlock()); | 
|  | while (workList.hasNext()) { | 
|  | BasicBlock block = workList.next(); | 
|  | for (Instruction instruction : block.getInstructions()) { | 
|  | switch (instruction.opcode()) { | 
|  | case ARGUMENT: | 
|  | case ASSUME: | 
|  | case CONST_CLASS: | 
|  | case CONST_NUMBER: | 
|  | case CONST_STRING: | 
|  | case DEX_ITEM_BASED_CONST_STRING: | 
|  | case RETURN: | 
|  | break; | 
|  |  | 
|  | case GOTO: | 
|  | if (!workList.addIfNotSeen(instruction.asGoto().getTarget())) { | 
|  | return invalid(); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case INSTANCE_PUT: | 
|  | { | 
|  | InstancePut instancePut = instruction.asInstancePut(); | 
|  |  | 
|  | // This must initialize a field on the receiver. | 
|  | if (!instancePut.object().getAliasedValue().isThis()) { | 
|  | return invalid(); | 
|  | } | 
|  |  | 
|  | // Check that this writes a field on the enclosing class. | 
|  | DexField fieldReference = instancePut.getField(); | 
|  | DexField lensRewrittenFieldReference = | 
|  | appView.graphLens().lookupField(fieldReference); | 
|  | if (lensRewrittenFieldReference.getHolderType() | 
|  | != instanceInitializer.getHolderType()) { | 
|  | return invalid(); | 
|  | } | 
|  |  | 
|  | ProgramField sourceField = | 
|  | instanceInitializer.getHolder().lookupProgramField(lensRewrittenFieldReference); | 
|  | if (sourceField == null) { | 
|  | return invalid(); | 
|  | } | 
|  |  | 
|  | InstanceFieldInitializationInfo initializationInfo = | 
|  | getInitializationInfo(appView, instancePut.value()); | 
|  | if (initializationInfo == null) { | 
|  | return invalid(); | 
|  | } | 
|  |  | 
|  | ProgramField targetField = group.getTargetInstanceField(sourceField); | 
|  | assert targetField != null; | 
|  |  | 
|  | builder.addInstancePut(targetField.getReference(), initializationInfo); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case INVOKE_DIRECT: | 
|  | { | 
|  | InvokeDirect invoke = instruction.asInvokeDirect(); | 
|  |  | 
|  | // This must initialize the receiver. | 
|  | if (!invoke.getReceiver().getAliasedValue().isThis()) { | 
|  | return invalid(); | 
|  | } | 
|  |  | 
|  | DexMethod invokedMethod = invoke.getInvokedMethod(); | 
|  | DexMethod lensRewrittenInvokedMethod = | 
|  | appView | 
|  | .graphLens() | 
|  | .lookupInvokeDirect(invokedMethod, instanceInitializer) | 
|  | .getReference(); | 
|  |  | 
|  | // TODO(b/189296638): Consider allowing constructor forwarding. | 
|  | if (!lensRewrittenInvokedMethod.isInstanceInitializer(appView.dexItemFactory()) | 
|  | || lensRewrittenInvokedMethod.getHolderType() != group.getSuperType()) { | 
|  | return invalid(); | 
|  | } | 
|  |  | 
|  | // Extract the non-receiver arguments. | 
|  | List<InstanceFieldInitializationInfo> arguments = | 
|  | new ArrayList<>(invoke.arguments().size() - 1); | 
|  | for (Value argument : Iterables.skip(invoke.arguments(), 1)) { | 
|  | InstanceFieldInitializationInfo initializationInfo = | 
|  | getInitializationInfo(appView, argument); | 
|  | if (initializationInfo == null) { | 
|  | return invalid(); | 
|  | } | 
|  | arguments.add(initializationInfo); | 
|  | } | 
|  |  | 
|  | if (!builder.addInvokeConstructor(invokedMethod, arguments)) { | 
|  | return invalid(); | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | // Not allowed. | 
|  | return invalid(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return builder.isValid() ? builder.build() : null; | 
|  | } | 
|  |  | 
|  | private static InstanceFieldInitializationInfo getInitializationInfo( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, Value value) { | 
|  | Value root = value.getAliasedValue(); | 
|  | if (root.isPhi()) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | Instruction definition = root.getDefinition(); | 
|  | if (definition.isArgument()) { | 
|  | return appView | 
|  | .instanceFieldInitializationInfoFactory() | 
|  | .createArgumentInitializationInfo(root.getDefinition().asArgument().getIndex()); | 
|  | } | 
|  | if (definition.isConstNumber()) { | 
|  | return appView | 
|  | .abstractValueFactory() | 
|  | .createSingleNumberValue(definition.asConstNumber().getRawValue()); | 
|  | } | 
|  | if (definition.isConstString()) { | 
|  | return appView | 
|  | .abstractValueFactory() | 
|  | .createSingleStringValue(definition.asConstString().getValue()); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private static InstanceInitializerDescription invalid() { | 
|  | return null; | 
|  | } | 
|  | } |