1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later,
9  * or the Apache License Version 2.0.
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  */
16 
17 package javassist.convert;
18 
19 import javassist.ClassPool;
20 import javassist.CtClass;
21 import javassist.CtMethod;
22 import javassist.Modifier;
23 import javassist.NotFoundException;
24 import javassist.bytecode.BadBytecode;
25 import javassist.bytecode.CodeAttribute;
26 import javassist.bytecode.CodeIterator;
27 import javassist.bytecode.ConstPool;
28 
29 public class TransformCall extends Transformer {
30     protected String classname, methodname, methodDescriptor;
31     protected String newClassname, newMethodname;
32     protected boolean newMethodIsPrivate;
33 
34     /* cache */
35     protected int newIndex;
36     protected ConstPool constPool;
37 
TransformCall(Transformer next, CtMethod origMethod, CtMethod substMethod)38     public TransformCall(Transformer next, CtMethod origMethod,
39                          CtMethod substMethod)
40     {
41         this(next, origMethod.getName(), substMethod);
42         classname = origMethod.getDeclaringClass().getName();
43     }
44 
TransformCall(Transformer next, String oldMethodName, CtMethod substMethod)45     public TransformCall(Transformer next, String oldMethodName,
46                          CtMethod substMethod)
47     {
48         super(next);
49         methodname = oldMethodName;
50         methodDescriptor = substMethod.getMethodInfo2().getDescriptor();
51         classname = newClassname = substMethod.getDeclaringClass().getName();
52         newMethodname = substMethod.getName();
53         constPool = null;
54         newMethodIsPrivate = Modifier.isPrivate(substMethod.getModifiers());
55     }
56 
57     @Override
initialize(ConstPool cp, CodeAttribute attr)58     public void initialize(ConstPool cp, CodeAttribute attr) {
59         if (constPool != cp)
60             newIndex = 0;
61     }
62 
63     /**
64      * Modify INVOKEINTERFACE, INVOKESPECIAL, INVOKESTATIC and INVOKEVIRTUAL
65      * so that a different method is invoked.  The class name in the operand
66      * of these instructions might be a subclass of the target class specified
67      * by <code>classname</code>.   This method transforms the instruction
68      * in that case unless the subclass overrides the target method.
69      */
70     @Override
transform(CtClass clazz, int pos, CodeIterator iterator, ConstPool cp)71     public int transform(CtClass clazz, int pos, CodeIterator iterator,
72                          ConstPool cp) throws BadBytecode
73     {
74         int c = iterator.byteAt(pos);
75         if (c == INVOKEINTERFACE || c == INVOKESPECIAL
76                         || c == INVOKESTATIC || c == INVOKEVIRTUAL) {
77             int index = iterator.u16bitAt(pos + 1);
78             String cname = cp.eqMember(methodname, methodDescriptor, index);
79             if (cname != null && matchClass(cname, clazz.getClassPool())) {
80                 int ntinfo = cp.getMemberNameAndType(index);
81                 pos = match(c, pos, iterator,
82                             cp.getNameAndTypeDescriptor(ntinfo), cp);
83             }
84         }
85 
86         return pos;
87     }
88 
matchClass(String name, ClassPool pool)89     private boolean matchClass(String name, ClassPool pool) {
90         if (classname.equals(name))
91             return true;
92 
93         try {
94             CtClass clazz = pool.get(name);
95             CtClass declClazz = pool.get(classname);
96             if (clazz.subtypeOf(declClazz))
97                 try {
98                     CtMethod m = clazz.getMethod(methodname, methodDescriptor);
99                     return m.getDeclaringClass().getName().equals(classname);
100                 }
101                 catch (NotFoundException e) {
102                     // maybe the original method has been removed.
103                     return true;
104                 }
105         }
106         catch (NotFoundException e) {
107             return false;
108         }
109 
110         return false;
111     }
112 
match(int c, int pos, CodeIterator iterator, int typedesc, ConstPool cp)113     protected int match(int c, int pos, CodeIterator iterator,
114                         int typedesc, ConstPool cp) throws BadBytecode
115     {
116         if (newIndex == 0) {
117             int nt = cp.addNameAndTypeInfo(cp.addUtf8Info(newMethodname),
118                                            typedesc);
119             int ci = cp.addClassInfo(newClassname);
120             if (c == INVOKEINTERFACE)
121                 newIndex = cp.addInterfaceMethodrefInfo(ci, nt);
122             else {
123                 if (newMethodIsPrivate && c == INVOKEVIRTUAL)
124                     iterator.writeByte(INVOKESPECIAL, pos);
125 
126                 newIndex = cp.addMethodrefInfo(ci, nt);
127             }
128 
129             constPool = cp;
130         }
131 
132         iterator.write16bit(newIndex, pos + 1);
133         return pos;
134     }
135 }
136