|  | // 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.graph; | 
|  |  | 
|  | import com.android.tools.r8.cf.CfPrinter; | 
|  | import com.android.tools.r8.cf.code.CfFrame; | 
|  | import com.android.tools.r8.cf.code.CfInstruction; | 
|  | import com.android.tools.r8.cf.code.CfLabel; | 
|  | import com.android.tools.r8.cf.code.CfPosition; | 
|  | import com.android.tools.r8.cf.code.CfReturnVoid; | 
|  | import com.android.tools.r8.cf.code.CfTryCatch; | 
|  | import com.android.tools.r8.errors.Unimplemented; | 
|  | import com.android.tools.r8.ir.code.IRCode; | 
|  | import com.android.tools.r8.ir.code.Position; | 
|  | import com.android.tools.r8.ir.code.ValueNumberGenerator; | 
|  | import com.android.tools.r8.ir.conversion.CfSourceCode; | 
|  | import com.android.tools.r8.ir.conversion.IRBuilder; | 
|  | import com.android.tools.r8.naming.ClassNameMapper; | 
|  | import com.android.tools.r8.naming.NamingLens; | 
|  | import com.android.tools.r8.origin.Origin; | 
|  | import com.android.tools.r8.utils.InternalOptions; | 
|  | import java.util.Collections; | 
|  | import java.util.List; | 
|  | import org.objectweb.asm.Label; | 
|  | import org.objectweb.asm.MethodVisitor; | 
|  |  | 
|  | public class CfCode extends Code { | 
|  |  | 
|  | public static class LocalVariableInfo { | 
|  | private final int index; | 
|  | private final DebugLocalInfo local; | 
|  | private final CfLabel start; | 
|  | private CfLabel end; | 
|  |  | 
|  | public LocalVariableInfo(int index, DebugLocalInfo local, CfLabel start) { | 
|  | this.index = index; | 
|  | this.local = local; | 
|  | this.start = start; | 
|  | } | 
|  |  | 
|  | public LocalVariableInfo(int index, DebugLocalInfo local, CfLabel start, CfLabel end) { | 
|  | this(index, local, start); | 
|  | setEnd(end); | 
|  | } | 
|  |  | 
|  | public void setEnd(CfLabel end) { | 
|  | assert this.end == null; | 
|  | assert end != null; | 
|  | this.end = end; | 
|  | } | 
|  |  | 
|  | public int getIndex() { | 
|  | return index; | 
|  | } | 
|  |  | 
|  | public DebugLocalInfo getLocal() { | 
|  | return local; | 
|  | } | 
|  |  | 
|  | public CfLabel getStart() { | 
|  | return start; | 
|  | } | 
|  |  | 
|  | public CfLabel getEnd() { | 
|  | return end; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "" + index + " => " + local; | 
|  | } | 
|  | } | 
|  |  | 
|  | private final int maxStack; | 
|  | private final int maxLocals; | 
|  | public final List<CfInstruction> instructions; | 
|  | private final List<CfTryCatch> tryCatchRanges; | 
|  | private final List<LocalVariableInfo> localVariables; | 
|  |  | 
|  | public CfCode( | 
|  | int maxStack, | 
|  | int maxLocals, | 
|  | List<CfInstruction> instructions, | 
|  | List<CfTryCatch> tryCatchRanges, | 
|  | List<LocalVariableInfo> localVariables) { | 
|  | this.maxStack = maxStack; | 
|  | this.maxLocals = maxLocals; | 
|  | this.instructions = instructions; | 
|  | this.tryCatchRanges = tryCatchRanges; | 
|  | this.localVariables = localVariables; | 
|  | } | 
|  |  | 
|  | public int getMaxStack() { | 
|  | return maxStack; | 
|  | } | 
|  |  | 
|  | public int getMaxLocals() { | 
|  | return maxLocals; | 
|  | } | 
|  |  | 
|  | public List<CfTryCatch> getTryCatchRanges() { | 
|  | return tryCatchRanges; | 
|  | } | 
|  |  | 
|  | public List<CfInstruction> getInstructions() { | 
|  | return Collections.unmodifiableList(instructions); | 
|  | } | 
|  |  | 
|  | public List<LocalVariableInfo> getLocalVariables() { | 
|  | return Collections.unmodifiableList(localVariables); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int estimatedSizeForInlining() { | 
|  | return countNonStackOperations(Integer.MAX_VALUE); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean estimatedSizeForInliningAtMost(int threshold) { | 
|  | return countNonStackOperations(threshold) <= threshold; | 
|  | } | 
|  |  | 
|  | private int countNonStackOperations(int threshold) { | 
|  | int result = 0; | 
|  | for (CfInstruction instruction : instructions) { | 
|  | if (instruction.emitsIR()) { | 
|  | result++; | 
|  | if (result > threshold) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isCfCode() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public CfCode asCfCode() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public void write( | 
|  | MethodVisitor visitor, NamingLens namingLens, InternalOptions options, int classFileVersion) { | 
|  | for (CfInstruction instruction : instructions) { | 
|  | if (instruction instanceof CfFrame | 
|  | && (classFileVersion <= 49 | 
|  | || (classFileVersion == 50 | 
|  | && !options.getProguardConfiguration().getKeepAttributes().stackMapTable))) { | 
|  | continue; | 
|  | } | 
|  | instruction.write(visitor, namingLens); | 
|  | } | 
|  | visitor.visitEnd(); | 
|  | visitor.visitMaxs(maxStack, maxLocals); | 
|  | for (CfTryCatch tryCatch : tryCatchRanges) { | 
|  | Label start = tryCatch.start.getLabel(); | 
|  | Label end = tryCatch.end.getLabel(); | 
|  | for (int i = 0; i < tryCatch.guards.size(); i++) { | 
|  | DexType guard = tryCatch.guards.get(i); | 
|  | Label target = tryCatch.targets.get(i).getLabel(); | 
|  | visitor.visitTryCatchBlock( | 
|  | start, | 
|  | end, | 
|  | target, | 
|  | guard == options.itemFactory.throwableType | 
|  | ? null | 
|  | : namingLens.lookupInternalName(guard)); | 
|  | } | 
|  | } | 
|  | for (LocalVariableInfo localVariable : localVariables) { | 
|  | DebugLocalInfo info = localVariable.local; | 
|  | visitor.visitLocalVariable( | 
|  | info.name.toString(), | 
|  | namingLens.lookupDescriptor(info.type).toString(), | 
|  | info.signature == null ? null : info.signature.toString(), | 
|  | localVariable.start.getLabel(), | 
|  | localVariable.end.getLabel(), | 
|  | localVariable.index); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected int computeHashCode() { | 
|  | throw new Unimplemented(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected boolean computeEquals(Object other) { | 
|  | throw new Unimplemented(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isEmptyVoidMethod() { | 
|  | for (CfInstruction insn : instructions) { | 
|  | if (!(insn instanceof CfReturnVoid) | 
|  | && !(insn instanceof CfLabel) | 
|  | && !(insn instanceof CfPosition)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public IRCode buildIR(DexEncodedMethod encodedMethod, AppView<?> appView, Origin origin) { | 
|  | return internalBuild(encodedMethod, encodedMethod, appView, null, null, origin); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public IRCode buildInliningIR( | 
|  | DexEncodedMethod context, | 
|  | DexEncodedMethod encodedMethod, | 
|  | AppView<?> appView, | 
|  | ValueNumberGenerator valueNumberGenerator, | 
|  | Position callerPosition, | 
|  | Origin origin) { | 
|  | assert valueNumberGenerator != null; | 
|  | assert callerPosition != null; | 
|  | return internalBuild( | 
|  | context, encodedMethod, appView, valueNumberGenerator, callerPosition, origin); | 
|  | } | 
|  |  | 
|  | private IRCode internalBuild( | 
|  | DexEncodedMethod context, | 
|  | DexEncodedMethod encodedMethod, | 
|  | AppView<?> appView, | 
|  | ValueNumberGenerator generator, | 
|  | Position callerPosition, | 
|  | Origin origin) { | 
|  | CfSourceCode source = | 
|  | new CfSourceCode( | 
|  | this, | 
|  | encodedMethod, | 
|  | appView.graphLense().getOriginalMethodSignature(encodedMethod.method), | 
|  | callerPosition, | 
|  | origin, | 
|  | appView); | 
|  | return new IRBuilder(encodedMethod, appView, source, origin, generator).build(context); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void registerCodeReferences(DexEncodedMethod method, UseRegistry registry) { | 
|  | for (CfInstruction instruction : instructions) { | 
|  | instruction.registerUse(registry, method.method.holder); | 
|  | } | 
|  | for (CfTryCatch tryCatch : tryCatchRanges) { | 
|  | for (DexType guard : tryCatch.guards) { | 
|  | registry.registerTypeReference(guard); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return new CfPrinter(this).toString(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString(DexEncodedMethod method, ClassNameMapper naming) { | 
|  | return new CfPrinter(this, method, naming).toString(); | 
|  | } | 
|  | } |