Home | History | Annotate | Download | only in code
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.dx.rop.code;
     17 
     18 import com.android.dx.rop.cst.CstMethodRef;
     19 import com.android.dx.rop.cst.CstNat;
     20 import com.android.dx.rop.cst.CstProtoRef;
     21 import com.android.dx.rop.cst.CstString;
     22 import com.android.dx.rop.cst.CstType;
     23 import com.android.dx.rop.type.Type;
     24 import com.android.dx.rop.type.TypeList;
     25 
     26 /**
     27  * An invoke-polymorphic instruction. This is a throwing instruction with
     28  * multiple constants.
     29  */
     30 public class InvokePolymorphicInsn extends Insn {
     31     /** Default descriptor for signature polymorphic methods. */
     32     private static final CstString DEFAULT_DESCRIPTOR =
     33             new CstString("([Ljava/lang/Object;)Ljava/lang/Object;");
     34 
     35     /** Descriptor for VarHandle set methods. */
     36     private static final CstString VARHANDLE_SET_DESCRIPTOR =
     37             new CstString("([Ljava/lang/Object;)V");
     38 
     39     /** Descriptor for VarHandle compare-and-set methods. */
     40     private static final CstString VARHANDLE_COMPARE_AND_SET_DESCRIPTOR =
     41             new CstString("([Ljava/lang/Object;)Z");
     42 
     43     /** {@code non-null;} list of exceptions caught */
     44     private final TypeList catches;
     45 
     46     /**
     47      * {@code non-null;} method as it appears at the call site of the original
     48      * invoke-virtual instruction. This is used to construct the invoke method
     49      * to target and the call-site prototype.
     50      */
     51     private final CstMethodRef callSiteMethod;
     52 
     53     /**
     54      * {@code non-null;} signature polymorphic method.
     55      */
     56     private final CstMethodRef polymorphicMethod;
     57 
     58     /**
     59      * {@code non-null;} the call site prototype.
     60      */
     61     private final CstProtoRef callSiteProto;
     62 
     63     /**
     64      * Constructs an instance.
     65      *
     66      * @param opcode {@code non-null;} the opcode
     67      * @param position {@code non-null;} source position
     68      * @param sources {@code non-null;} specs for all the sources
     69      * @param catches {@code non-null;} list of exceptions caught
     70      * @param callSiteMethod {@code non-null;} the method called by
     71      * invoke-virtual that this instance will replace.
     72      */
     73     public InvokePolymorphicInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, TypeList catches,
     74             CstMethodRef callSiteMethod) {
     75         super(opcode, position, null, sources);
     76 
     77         if (opcode.getBranchingness() != Rop.BRANCH_THROW) {
     78             throw new IllegalArgumentException("opcode with invalid branchingness: " + opcode.getBranchingness());
     79         }
     80 
     81         if (catches == null) {
     82             throw new NullPointerException("catches == null");
     83         }
     84         this.catches = catches;
     85 
     86         if (callSiteMethod == null) {
     87             throw new NullPointerException("callSiteMethod == null");
     88         } else if (!callSiteMethod.isSignaturePolymorphic()) {
     89             throw new IllegalArgumentException("callSiteMethod is not signature polymorphic");
     90         }
     91 
     92         this.callSiteMethod = callSiteMethod;
     93         this.polymorphicMethod = makePolymorphicMethod(callSiteMethod);
     94         this.callSiteProto = makeCallSiteProto(callSiteMethod);
     95     }
     96 
     97     /** {@inheritDoc} */
     98     @Override
     99     public TypeList getCatches() {
    100         return this.catches;
    101     }
    102 
    103     /** {@inheritDoc} */
    104     @Override
    105     public void accept(Visitor visitor) {
    106         visitor.visitInvokePolymorphicInsn(this);
    107     }
    108 
    109     /** {@inheritDoc} */
    110     @Override
    111     public Insn withAddedCatch(Type type) {
    112         return new InvokePolymorphicInsn(getOpcode(), getPosition(),
    113                 getSources(), catches.withAddedType(type), getCallSiteMethod());
    114     }
    115 
    116     /** {@inheritDoc} */
    117     @Override
    118     public Insn withRegisterOffset(int delta) {
    119         return new InvokePolymorphicInsn(getOpcode(), getPosition(),
    120                 getSources().withOffset(delta),
    121                 catches, getCallSiteMethod());
    122     }
    123 
    124     /** {@inheritDoc} */
    125     @Override
    126     public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) {
    127         return new InvokePolymorphicInsn(getOpcode(), getPosition(),
    128                 sources, catches, getCallSiteMethod());
    129     }
    130 
    131     /**
    132      * Gets the method as it appears at the call site of the original
    133      * invoke-virtual instruction.
    134      *
    135      * @return {@code non-null;} the original method reference
    136      */
    137     public CstMethodRef getCallSiteMethod() {
    138         return callSiteMethod;
    139     }
    140 
    141     /**
    142      * Gets the method to be invoked. This will be will either be
    143      * {@code java.lang.invoke.MethodHandle.invoke()} or
    144      * {@code java.lang.invoke.MethodHandle.invokeExact()}.
    145      *
    146      * @return {@code non-null;} method reference to be invoked
    147      */
    148     public CstMethodRef getPolymorphicMethod() {
    149         return polymorphicMethod;
    150     }
    151 
    152     /**
    153      * Gets the call site prototype. The call site prototype is provided
    154      * as an argument to invoke-polymorphic to enable type checking and
    155      * type conversion.
    156      *
    157      * @return {@code non-null;} Prototype reference for call site
    158      */
    159     public CstProtoRef getCallSiteProto() {
    160         return callSiteProto;
    161     }
    162 
    163     /** {@inheritDoc} */
    164     @Override
    165     public String getInlineString() {
    166         return getPolymorphicMethod().toString() + " " +
    167             getCallSiteProto().toString() + " " +
    168             ThrowingInsn.toCatchString(catches);
    169     }
    170 
    171     private static CstMethodRef makePolymorphicMethod(final CstMethodRef callSiteMethod) {
    172         CstType definingClass= callSiteMethod.getDefiningClass();
    173         CstString cstMethodName = callSiteMethod.getNat().getName();
    174         String methodName = callSiteMethod.getNat().getName().getString();
    175 
    176         if (definingClass.equals(CstType.METHOD_HANDLE)) {
    177             if (methodName.equals("invoke") || methodName.equals("invokeExact")) {
    178                 CstNat cstNat = new CstNat(cstMethodName, DEFAULT_DESCRIPTOR);
    179                 return new CstMethodRef(definingClass, cstNat);
    180             }
    181         }
    182 
    183         if (definingClass.equals(CstType.VAR_HANDLE)) {
    184             switch (methodName) {
    185                 case "compareAndExchange":
    186                 case "compareAndExchangeAcquire":
    187                 case "compareAndExchangeRelease":
    188                 case "get":
    189                 case "getAcquire":
    190                 case "getAndAdd":
    191                 case "getAndAddAcquire":
    192                 case "getAndAddRelease":
    193                 case "getAndBitwiseAnd":
    194                 case "getAndBitwiseAndAcquire":
    195                 case "getAndBitwiseAndRelease":
    196                 case "getAndBitwiseOr":
    197                 case "getAndBitwiseOrAcquire":
    198                 case "getAndBitwiseOrRelease":
    199                 case "getAndBitwiseXor":
    200                 case "getAndBitwiseXorAcquire":
    201                 case "getAndBitwiseXorRelease":
    202                 case "getAndSet":
    203                 case "getAndSetAcquire":
    204                 case "getAndSetRelease":
    205                 case "getOpaque":
    206                 case "getVolatile":
    207                 {
    208                     CstNat cstNat = new CstNat(cstMethodName, DEFAULT_DESCRIPTOR);
    209                     return new CstMethodRef(definingClass, cstNat);
    210                 }
    211                 case "set":
    212                 case "setOpaque":
    213                 case "setRelease":
    214                 case "setVolatile":
    215                 {
    216                     CstNat cstNat = new CstNat(cstMethodName, VARHANDLE_SET_DESCRIPTOR);
    217                     return new CstMethodRef(definingClass, cstNat);
    218                 }
    219                 case "compareAndSet":
    220                 case "weakCompareAndSet":
    221                 case "weakCompareAndSetAcquire":
    222                 case "weakCompareAndSetPlain":
    223                 case "weakCompareAndSetRelease":
    224                 {
    225                     CstNat cstNat = new CstNat(cstMethodName, VARHANDLE_COMPARE_AND_SET_DESCRIPTOR);
    226                     return new CstMethodRef(definingClass, cstNat);
    227                 }
    228                 default:
    229                     break;
    230             }
    231         }
    232         throw new IllegalArgumentException("Unknown signature polymorphic method: " +
    233                                            callSiteMethod.toHuman());
    234     }
    235 
    236     private static CstProtoRef makeCallSiteProto(final CstMethodRef callSiteMethod) {
    237         return new CstProtoRef(callSiteMethod.getPrototype(true));
    238     }
    239 }
    240