| // Copyright (c) 2019, 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.cf.code.CfArithmeticBinop; |
| import com.android.tools.r8.cf.code.CfArithmeticBinop.Opcode; |
| import com.android.tools.r8.cf.code.CfArrayLength; |
| import com.android.tools.r8.cf.code.CfArrayLoad; |
| import com.android.tools.r8.cf.code.CfArrayStore; |
| import com.android.tools.r8.cf.code.CfCheckCast; |
| import com.android.tools.r8.cf.code.CfConstNull; |
| import com.android.tools.r8.cf.code.CfConstNumber; |
| import com.android.tools.r8.cf.code.CfConstString; |
| import com.android.tools.r8.cf.code.CfFrame; |
| import com.android.tools.r8.cf.code.CfFrame.FrameType; |
| import com.android.tools.r8.cf.code.CfGoto; |
| import com.android.tools.r8.cf.code.CfIf; |
| import com.android.tools.r8.cf.code.CfIfCmp; |
| import com.android.tools.r8.cf.code.CfInstanceFieldRead; |
| import com.android.tools.r8.cf.code.CfInstanceFieldWrite; |
| import com.android.tools.r8.cf.code.CfInstanceOf; |
| import com.android.tools.r8.cf.code.CfInstruction; |
| import com.android.tools.r8.cf.code.CfInvoke; |
| import com.android.tools.r8.cf.code.CfLabel; |
| import com.android.tools.r8.cf.code.CfLoad; |
| import com.android.tools.r8.cf.code.CfNew; |
| import com.android.tools.r8.cf.code.CfNewArray; |
| import com.android.tools.r8.cf.code.CfReturn; |
| import com.android.tools.r8.cf.code.CfReturnVoid; |
| import com.android.tools.r8.cf.code.CfStackInstruction; |
| import com.android.tools.r8.cf.code.CfStaticFieldRead; |
| import com.android.tools.r8.cf.code.CfStore; |
| import com.android.tools.r8.cf.code.CfThrow; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.CfCode; |
| import com.android.tools.r8.graph.DexEncodedField; |
| import com.android.tools.r8.graph.DexField; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexProto; |
| import com.android.tools.r8.graph.DexString; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.ir.code.If; |
| import com.android.tools.r8.ir.code.MemberType; |
| import com.android.tools.r8.ir.code.NumericType; |
| import com.android.tools.r8.ir.code.ValueType; |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter; |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizer; |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer; |
| import com.android.tools.r8.utils.BooleanUtils; |
| import com.android.tools.r8.utils.collections.ImmutableDeque; |
| import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import org.objectweb.asm.Opcodes; |
| |
| public abstract class DesugaredLibraryAPIConversionCfCodeProvider extends SyntheticCfCodeProvider { |
| |
| DesugaredLibraryAPIConversionCfCodeProvider(AppView<?> appView, DexType holder) { |
| super(appView, holder); |
| } |
| |
| DexType vivifiedTypeFor(DexType type) { |
| return DesugaredLibraryAPIConverter.vivifiedTypeFor(type, appView); |
| } |
| |
| public static class APIConverterVivifiedWrapperCfCodeProvider |
| extends DesugaredLibraryAPIConversionCfCodeProvider { |
| |
| private final DexField wrapperField; |
| private final DexMethod forwardMethod; |
| private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizer; |
| private final boolean itfCall; |
| |
| public APIConverterVivifiedWrapperCfCodeProvider( |
| AppView<?> appView, |
| DexMethod forwardMethod, |
| DexField wrapperField, |
| DesugaredLibraryWrapperSynthesizer wrapperSynthesizer, |
| boolean itfCall) { |
| super(appView, wrapperField.holder); |
| this.forwardMethod = forwardMethod; |
| this.wrapperField = wrapperField; |
| this.wrapperSynthesizer = wrapperSynthesizer; |
| this.itfCall = itfCall; |
| } |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexItemFactory factory = appView.dexItemFactory(); |
| List<CfInstruction> instructions = new ArrayList<>(); |
| // Wrapped value is a vivified type. Method uses type as external. Forward method should |
| // use vivifiedTypes. |
| |
| instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); |
| instructions.add(new CfInstanceFieldRead(wrapperField)); |
| int index = 1; |
| int stackIndex = 1; |
| DexType[] newParameters = forwardMethod.proto.parameters.values.clone(); |
| for (DexType param : forwardMethod.proto.parameters.values) { |
| instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex)); |
| if (wrapperSynthesizer.shouldConvert(param, forwardMethod)) { |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESTATIC, |
| wrapperSynthesizer.getExistingProgramConversionMethod( |
| param, param, vivifiedTypeFor(param)), |
| false)); |
| newParameters[index - 1] = vivifiedTypeFor(param); |
| } |
| if (param == factory.longType || param == factory.doubleType) { |
| stackIndex++; |
| } |
| stackIndex++; |
| index++; |
| } |
| |
| DexType returnType = forwardMethod.proto.returnType; |
| DexType forwardMethodReturnType = |
| appView.rewritePrefix.hasRewrittenType(returnType, appView) |
| ? vivifiedTypeFor(returnType) |
| : returnType; |
| |
| DexProto newProto = factory.createProto(forwardMethodReturnType, newParameters); |
| DexMethod newForwardMethod = |
| factory.createMethod(wrapperField.type, newProto, forwardMethod.name); |
| |
| if (itfCall) { |
| instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, newForwardMethod, true)); |
| } else { |
| instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newForwardMethod, false)); |
| } |
| |
| if (wrapperSynthesizer.shouldConvert(returnType, forwardMethod)) { |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESTATIC, |
| wrapperSynthesizer.getExistingProgramConversionMethod( |
| returnType, vivifiedTypeFor(returnType), returnType), |
| false)); |
| } |
| if (returnType == factory.voidType) { |
| instructions.add(new CfReturnVoid()); |
| } else { |
| instructions.add(new CfReturn(ValueType.fromDexType(returnType))); |
| } |
| return standardCfCodeFromInstructions(instructions); |
| } |
| } |
| |
| public abstract static class AbstractAPIConverterWrapperCfCodeProvider |
| extends DesugaredLibraryAPIConversionCfCodeProvider { |
| |
| DexMethod forwardMethod; |
| DesugaredLibraryWrapperSynthesizer wrapperSynthesizor; |
| boolean itfCall; |
| |
| public AbstractAPIConverterWrapperCfCodeProvider( |
| AppView<?> appView, |
| DexType holder, |
| DexMethod forwardMethod, |
| DesugaredLibraryWrapperSynthesizer wrapperSynthesizor, |
| boolean itfCall) { |
| super(appView, holder); |
| this.forwardMethod = forwardMethod; |
| this.wrapperSynthesizor = wrapperSynthesizor; |
| this.itfCall = itfCall; |
| } |
| |
| abstract void generatePushReceiver(List<CfInstruction> instructions); |
| |
| abstract DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType); |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexItemFactory factory = appView.dexItemFactory(); |
| List<CfInstruction> instructions = new ArrayList<>(); |
| // Wrapped value is a type. Method uses vivifiedTypes as external. Forward method should |
| // use types. |
| |
| generatePushReceiver(instructions); |
| int stackIndex = 1; |
| for (DexType param : forwardMethod.proto.parameters.values) { |
| instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex)); |
| if (wrapperSynthesizor.shouldConvert(param, forwardMethod)) { |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESTATIC, |
| ensureConversionMethod(param, vivifiedTypeFor(param), param), |
| false)); |
| } |
| if (param == factory.longType || param == factory.doubleType) { |
| stackIndex++; |
| } |
| stackIndex++; |
| } |
| |
| if (itfCall) { |
| instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, forwardMethod, true)); |
| } else { |
| instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, forwardMethod, false)); |
| } |
| |
| DexType returnType = forwardMethod.proto.returnType; |
| if (wrapperSynthesizor.shouldConvert(returnType, forwardMethod)) { |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESTATIC, |
| ensureConversionMethod(returnType, returnType, vivifiedTypeFor(returnType)), |
| false)); |
| returnType = vivifiedTypeFor(returnType); |
| } |
| if (returnType == factory.voidType) { |
| instructions.add(new CfReturnVoid()); |
| } else { |
| instructions.add(new CfReturn(ValueType.fromDexType(returnType))); |
| } |
| return standardCfCodeFromInstructions(instructions); |
| } |
| } |
| |
| public static class APICallbackWrapperCfCodeProvider |
| extends AbstractAPIConverterWrapperCfCodeProvider { |
| |
| private final DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer; |
| |
| public APICallbackWrapperCfCodeProvider( |
| AppView<?> appView, |
| DexMethod forwardMethod, |
| DesugaredLibraryWrapperSynthesizer wrapperSynthesizor, |
| boolean itfCall, |
| DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) { |
| super(appView, forwardMethod.holder, forwardMethod, wrapperSynthesizor, itfCall); |
| this.eventConsumer = eventConsumer; |
| } |
| |
| @Override |
| void generatePushReceiver(List<CfInstruction> instructions) { |
| instructions.add(new CfLoad(ValueType.fromDexType(forwardMethod.holder), 0)); |
| } |
| |
| @Override |
| DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) { |
| return wrapperSynthesizor.ensureConversionMethod(type, srcType, destType, eventConsumer); |
| } |
| } |
| |
| public static class APIConverterWrapperCfCodeProvider |
| extends AbstractAPIConverterWrapperCfCodeProvider { |
| |
| private final DexField wrapperField; |
| |
| public APIConverterWrapperCfCodeProvider( |
| AppView<?> appView, |
| DexMethod forwardMethod, |
| DexField wrapperField, |
| DesugaredLibraryWrapperSynthesizer wrapperSynthesizor, |
| boolean itfCall) { |
| super(appView, wrapperField.holder, forwardMethod, wrapperSynthesizor, itfCall); |
| this.wrapperField = wrapperField; |
| } |
| |
| @Override |
| void generatePushReceiver(List<CfInstruction> instructions) { |
| instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); |
| instructions.add(new CfInstanceFieldRead(wrapperField)); |
| } |
| |
| @Override |
| DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) { |
| return wrapperSynthesizor.getExistingProgramConversionMethod(type, srcType, destType); |
| } |
| } |
| |
| public static class APIConverterWrapperConversionCfCodeProvider extends SyntheticCfCodeProvider { |
| |
| DexField reverseWrapperField; |
| DexField wrapperField; |
| |
| public APIConverterWrapperConversionCfCodeProvider( |
| AppView<?> appView, DexField reverseWrapperField, DexField wrapperField) { |
| super(appView, wrapperField.holder); |
| this.reverseWrapperField = reverseWrapperField; |
| this.wrapperField = wrapperField; |
| } |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexItemFactory factory = appView.dexItemFactory(); |
| List<CfInstruction> instructions = new ArrayList<>(); |
| |
| DexType argType = wrapperField.type; |
| ImmutableInt2ReferenceSortedMap<FrameType> locals = |
| ImmutableInt2ReferenceSortedMap.<FrameType>builder() |
| .put(0, FrameType.initialized(argType)) |
| .build(); |
| |
| // if (arg == null) { return null }; |
| CfLabel nullDest = new CfLabel(); |
| instructions.add(new CfLoad(ValueType.fromDexType(argType), 0)); |
| instructions.add(new CfIf(If.Type.NE, ValueType.OBJECT, nullDest)); |
| instructions.add(new CfConstNull()); |
| instructions.add(new CfReturn(ValueType.OBJECT)); |
| instructions.add(nullDest); |
| instructions.add(new CfFrame(locals, ImmutableDeque.of())); |
| |
| // if (arg instanceOf ReverseWrapper) { return ((ReverseWrapper) arg).wrapperField}; |
| assert reverseWrapperField != null; |
| CfLabel unwrapDest = new CfLabel(); |
| instructions.add(new CfLoad(ValueType.fromDexType(argType), 0)); |
| instructions.add(new CfInstanceOf(reverseWrapperField.holder)); |
| instructions.add(new CfIf(If.Type.EQ, ValueType.INT, unwrapDest)); |
| instructions.add(new CfLoad(ValueType.fromDexType(argType), 0)); |
| instructions.add(new CfCheckCast(reverseWrapperField.holder)); |
| instructions.add(new CfInstanceFieldRead(reverseWrapperField)); |
| instructions.add(new CfReturn(ValueType.fromDexType(reverseWrapperField.type))); |
| instructions.add(unwrapDest); |
| instructions.add(new CfFrame(locals, ImmutableDeque.of())); |
| |
| // return new Wrapper(wrappedValue); |
| instructions.add(new CfNew(wrapperField.holder)); |
| instructions.add(CfStackInstruction.fromAsm(Opcodes.DUP)); |
| instructions.add(new CfLoad(ValueType.fromDexType(argType), 0)); |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESPECIAL, |
| factory.createMethod( |
| wrapperField.holder, |
| factory.createProto(factory.voidType, argType), |
| factory.constructorMethodName), |
| false)); |
| instructions.add(new CfReturn(ValueType.fromDexType(wrapperField.holder))); |
| return standardCfCodeFromInstructions(instructions); |
| } |
| } |
| |
| public static class APIConversionCfCodeProvider extends SyntheticCfCodeProvider { |
| |
| private final CfInvoke initialInvoke; |
| private final DexMethod returnConversion; |
| private final DexMethod[] parameterConversions; |
| |
| public APIConversionCfCodeProvider( |
| AppView<?> appView, |
| DexType holder, |
| CfInvoke initialInvoke, |
| DexMethod returnConversion, |
| DexMethod[] parameterConversions) { |
| super(appView, holder); |
| this.initialInvoke = initialInvoke; |
| this.returnConversion = returnConversion; |
| this.parameterConversions = parameterConversions; |
| } |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexMethod invokedMethod = initialInvoke.getMethod(); |
| DexMethod convertedMethod = |
| DesugaredLibraryAPIConverter.getConvertedAPI( |
| invokedMethod, returnConversion, parameterConversions, appView); |
| |
| List<CfInstruction> instructions = new ArrayList<>(); |
| |
| boolean isStatic = initialInvoke.getOpcode() == Opcodes.INVOKESTATIC; |
| if (!isStatic) { |
| instructions.add(new CfLoad(ValueType.fromDexType(invokedMethod.holder), 0)); |
| } |
| int receiverShift = BooleanUtils.intValue(!isStatic); |
| int stackIndex = 0; |
| for (int i = 0; i < invokedMethod.getArity(); i++) { |
| DexType param = invokedMethod.getParameter(i); |
| instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex + receiverShift)); |
| if (parameterConversions[i] != null) { |
| instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, parameterConversions[i], false)); |
| } |
| if (param == appView.dexItemFactory().longType |
| || param == appView.dexItemFactory().doubleType) { |
| stackIndex++; |
| } |
| stackIndex++; |
| } |
| |
| // Actual call to converted value. |
| instructions.add( |
| new CfInvoke(initialInvoke.getOpcode(), convertedMethod, initialInvoke.isInterface())); |
| |
| // Return conversion. |
| if (returnConversion != null) { |
| instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, returnConversion, false)); |
| } |
| |
| if (invokedMethod.getReturnType().isVoidType()) { |
| instructions.add(new CfReturnVoid()); |
| } else { |
| instructions.add(new CfReturn(ValueType.fromDexType(invokedMethod.getReturnType()))); |
| } |
| return standardCfCodeFromInstructions(instructions); |
| } |
| } |
| |
| public static class EnumArrayConversionCfCodeProvider extends SyntheticCfCodeProvider { |
| |
| private final DexType enumType; |
| private final DexType convertedType; |
| |
| public EnumArrayConversionCfCodeProvider( |
| AppView<?> appView, DexType holder, DexType enumType, DexType convertedType) { |
| super(appView, holder); |
| this.enumType = enumType; |
| this.convertedType = convertedType; |
| } |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexItemFactory factory = appView.dexItemFactory(); |
| List<CfInstruction> instructions = new ArrayList<>(); |
| DexType enumTypeArray = factory.createArrayType(1, enumType); |
| DexType convertedTypeArray = factory.createArrayType(1, convertedType); |
| |
| // if (arg == null) { return null; } |
| instructions.add(new CfLoad(ValueType.fromDexType(enumTypeArray), 0)); |
| instructions.add(new CfConstNull()); |
| CfLabel nonNull = new CfLabel(); |
| instructions.add(new CfIfCmp(If.Type.NE, ValueType.OBJECT, nonNull)); |
| instructions.add(new CfConstNull()); |
| instructions.add(new CfReturn(ValueType.fromDexType(convertedTypeArray))); |
| instructions.add(nonNull); |
| instructions.add( |
| new CfFrame( |
| ImmutableInt2ReferenceSortedMap.<FrameType>builder() |
| .put(0, FrameType.initialized(enumTypeArray)) |
| .build(), |
| ImmutableDeque.of())); |
| |
| ImmutableInt2ReferenceSortedMap<FrameType> locals = |
| ImmutableInt2ReferenceSortedMap.<FrameType>builder() |
| .put(0, FrameType.initialized(enumTypeArray)) |
| .put(1, FrameType.initialized(factory.intType)) |
| .put(2, FrameType.initialized(convertedTypeArray)) |
| .put(3, FrameType.initialized(factory.intType)) |
| .build(); |
| |
| // int t1 = arg.length; |
| instructions.add(new CfLoad(ValueType.fromDexType(enumTypeArray), 0)); |
| instructions.add(new CfArrayLength()); |
| instructions.add(new CfStore(ValueType.INT, 1)); |
| // ConvertedType[] t2 = new ConvertedType[t1]; |
| instructions.add(new CfLoad(ValueType.INT, 1)); |
| instructions.add(new CfNewArray(convertedTypeArray)); |
| instructions.add(new CfStore(ValueType.fromDexType(convertedTypeArray), 2)); |
| // int t3 = 0; |
| instructions.add(new CfConstNumber(0, ValueType.INT)); |
| instructions.add(new CfStore(ValueType.INT, 3)); |
| // while (t3 < t1) { |
| CfLabel returnLabel = new CfLabel(); |
| CfLabel loopLabel = new CfLabel(); |
| instructions.add(loopLabel); |
| instructions.add(new CfFrame(locals, ImmutableDeque.of())); |
| instructions.add(new CfLoad(ValueType.INT, 3)); |
| instructions.add(new CfLoad(ValueType.INT, 1)); |
| instructions.add(new CfIfCmp(If.Type.GE, ValueType.INT, returnLabel)); |
| // t2[t3] = convert(arg[t3]); |
| instructions.add(new CfLoad(ValueType.fromDexType(convertedTypeArray), 2)); |
| instructions.add(new CfLoad(ValueType.INT, 3)); |
| instructions.add(new CfLoad(ValueType.fromDexType(enumTypeArray), 0)); |
| instructions.add(new CfLoad(ValueType.INT, 3)); |
| instructions.add(new CfArrayLoad(MemberType.OBJECT)); |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESTATIC, |
| factory.createMethod( |
| getHolder(), |
| factory.createProto(convertedType, enumType), |
| factory.convertMethodName), |
| false)); |
| instructions.add(new CfArrayStore(MemberType.OBJECT)); |
| // t3 = t3 + 1; } |
| instructions.add(new CfLoad(ValueType.INT, 3)); |
| instructions.add(new CfConstNumber(1, ValueType.INT)); |
| instructions.add(new CfArithmeticBinop(Opcode.Add, NumericType.INT)); |
| instructions.add(new CfStore(ValueType.INT, 3)); |
| instructions.add(new CfGoto(loopLabel)); |
| // return t2; |
| instructions.add(returnLabel); |
| instructions.add(new CfFrame(locals, ImmutableDeque.of())); |
| instructions.add(new CfLoad(ValueType.fromDexType(convertedTypeArray), 2)); |
| instructions.add(new CfReturn(ValueType.fromDexType(convertedTypeArray))); |
| return standardCfCodeFromInstructions(instructions); |
| } |
| } |
| |
| public static class EnumConversionCfCodeProvider extends SyntheticCfCodeProvider { |
| |
| private final Iterable<DexEncodedField> enumFields; |
| private final DexType enumType; |
| private final DexType convertedType; |
| |
| public EnumConversionCfCodeProvider( |
| AppView<?> appView, |
| DexType holder, |
| Iterable<DexEncodedField> enumFields, |
| DexType enumType, |
| DexType convertedType) { |
| super(appView, holder); |
| this.enumFields = enumFields; |
| this.enumType = enumType; |
| this.convertedType = convertedType; |
| } |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexItemFactory factory = appView.dexItemFactory(); |
| List<CfInstruction> instructions = new ArrayList<>(); |
| |
| ImmutableInt2ReferenceSortedMap<FrameType> locals = |
| ImmutableInt2ReferenceSortedMap.<FrameType>builder() |
| .put(0, FrameType.initialized(enumType)) |
| .build(); |
| |
| // if (arg == null) { return null; } |
| instructions.add(new CfLoad(ValueType.fromDexType(enumType), 0)); |
| instructions.add(new CfConstNull()); |
| CfLabel nonNull = new CfLabel(); |
| instructions.add(new CfIfCmp(If.Type.NE, ValueType.OBJECT, nonNull)); |
| instructions.add(new CfConstNull()); |
| instructions.add(new CfReturn(ValueType.fromDexType(convertedType))); |
| instructions.add(nonNull); |
| instructions.add(new CfFrame(locals, ImmutableDeque.of())); |
| |
| // if (arg == enumType.enumField1) { return convertedType.enumField1; } |
| Iterator<DexEncodedField> iterator = enumFields.iterator(); |
| while (iterator.hasNext()) { |
| DexEncodedField enumField = iterator.next(); |
| CfLabel notEqual = new CfLabel(); |
| if (iterator.hasNext()) { |
| instructions.add(new CfLoad(ValueType.fromDexType(enumType), 0)); |
| instructions.add( |
| new CfStaticFieldRead(factory.createField(enumType, enumType, enumField.getName()))); |
| instructions.add(new CfIfCmp(If.Type.NE, ValueType.OBJECT, notEqual)); |
| } |
| instructions.add( |
| new CfStaticFieldRead( |
| factory.createField(convertedType, convertedType, enumField.getName()))); |
| instructions.add(new CfReturn(ValueType.fromDexType(convertedType))); |
| if (iterator.hasNext()) { |
| instructions.add(notEqual); |
| instructions.add(new CfFrame(locals, ImmutableDeque.of())); |
| } |
| } |
| return standardCfCodeFromInstructions(instructions); |
| } |
| } |
| |
| public static class APIConverterConstructorCfCodeProvider extends SyntheticCfCodeProvider { |
| |
| DexField wrapperField; |
| |
| public APIConverterConstructorCfCodeProvider(AppView<?> appView, DexField wrapperField) { |
| super(appView, wrapperField.holder); |
| this.wrapperField = wrapperField; |
| } |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexItemFactory factory = appView.dexItemFactory(); |
| List<CfInstruction> instructions = new ArrayList<>(); |
| instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESPECIAL, |
| factory.createMethod( |
| factory.objectType, |
| factory.createProto(factory.voidType), |
| factory.constructorMethodName), |
| false)); |
| instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); |
| instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.type), 1)); |
| instructions.add(new CfInstanceFieldWrite(wrapperField)); |
| instructions.add(new CfReturnVoid()); |
| return standardCfCodeFromInstructions(instructions); |
| } |
| } |
| |
| public static class APIConverterThrowRuntimeExceptionCfCodeProvider |
| extends SyntheticCfCodeProvider { |
| |
| DexString message; |
| |
| public APIConverterThrowRuntimeExceptionCfCodeProvider( |
| AppView<?> appView, DexString message, DexType holder) { |
| super(appView, holder); |
| this.message = message; |
| } |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexItemFactory factory = appView.dexItemFactory(); |
| List<CfInstruction> instructions = new ArrayList<>(); |
| instructions.add(new CfNew(factory.runtimeExceptionType)); |
| instructions.add(CfStackInstruction.fromAsm(Opcodes.DUP)); |
| instructions.add(new CfConstString(message)); |
| instructions.add( |
| new CfInvoke( |
| Opcodes.INVOKESPECIAL, |
| factory.createMethod( |
| factory.runtimeExceptionType, |
| factory.createProto(factory.voidType, factory.stringType), |
| factory.constructorMethodName), |
| false)); |
| instructions.add(new CfThrow()); |
| return standardCfCodeFromInstructions(instructions); |
| } |
| } |
| } |