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