001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.asm;
016    
017    import com.liferay.portal.kernel.util.CharPool;
018    
019    import java.io.IOException;
020    
021    import java.util.ArrayList;
022    import java.util.List;
023    import java.util.Set;
024    
025    import org.objectweb.asm.ClassReader;
026    import org.objectweb.asm.ClassVisitor;
027    import org.objectweb.asm.MethodVisitor;
028    import org.objectweb.asm.Opcodes;
029    import org.objectweb.asm.Type;
030    import org.objectweb.asm.commons.AdviceAdapter;
031    import org.objectweb.asm.commons.Remapper;
032    import org.objectweb.asm.commons.RemappingClassAdapter;
033    import org.objectweb.asm.tree.AnnotationNode;
034    import org.objectweb.asm.tree.ClassNode;
035    import org.objectweb.asm.tree.FieldNode;
036    import org.objectweb.asm.tree.MethodNode;
037    
038    /**
039     * @author Shuyang Zhou
040     */
041    public class ASMUtil {
042    
043            public static void addDefaultReturnInsns(
044                    MethodVisitor methodVisitor, Type returnType) {
045    
046                    int sort = returnType.getSort();
047    
048                    if ((sort == Type.BOOLEAN) || (sort == Type.CHAR) ||
049                            (sort == Type.BYTE) || (sort == Type.INT) ||
050                            (sort == Type.SHORT)) {
051    
052                            methodVisitor.visitInsn(Opcodes.ICONST_0);
053                            methodVisitor.visitInsn(Opcodes.IRETURN);
054                    }
055                    else if (sort == Type.DOUBLE) {
056                            methodVisitor.visitInsn(Opcodes.DCONST_0);
057                            methodVisitor.visitInsn(Opcodes.DRETURN);
058                    }
059                    else if (sort == Type.FLOAT) {
060                            methodVisitor.visitInsn(Opcodes.FCONST_0);
061                            methodVisitor.visitInsn(Opcodes.FRETURN);
062                    }
063                    else if (sort == Type.LONG) {
064                            methodVisitor.visitInsn(Opcodes.LCONST_0);
065                            methodVisitor.visitInsn(Opcodes.LRETURN);
066                    }
067                    else if (sort == Type.VOID) {
068                            methodVisitor.visitInsn(Opcodes.RETURN);
069                    }
070                    else {
071                            methodVisitor.visitInsn(Opcodes.ACONST_NULL);
072                            methodVisitor.visitInsn(Opcodes.ARETURN);
073                    }
074            }
075    
076            public static List<FieldNode> addFieldNodes(
077                    List<FieldNode> fieldNodes, List<FieldNode> newFieldNodes) {
078    
079                    List<FieldNode> addedFieldNodes = new ArrayList<FieldNode>();
080    
081                    newFieldNode:
082                    for (FieldNode newFieldNode : newFieldNodes) {
083                            String newFieldNodeName = newFieldNode.name;
084    
085                            for (FieldNode fieldNode : fieldNodes) {
086                                    if (newFieldNodeName.equals(fieldNode.name)) {
087                                            continue newFieldNode;
088                                    }
089                            }
090    
091                            addedFieldNodes.add(newFieldNode);
092                    }
093    
094                    fieldNodes.addAll(addedFieldNodes);
095    
096                    return addedFieldNodes;
097            }
098    
099            public static FieldNode findFieldNode(
100                    List<FieldNode> fieldNodes, String name) {
101    
102                    for (FieldNode fieldNode : fieldNodes) {
103                            if (name.equals(fieldNode.name)) {
104                                    return fieldNode;
105                            }
106                    }
107    
108                    return null;
109            }
110    
111            public static MethodNode findMethodNode(
112                    List<MethodNode> methodNodes, String name, Type returnType,
113                    Type... argumentTypes) {
114    
115                    String desc = Type.getMethodDescriptor(returnType, argumentTypes);
116    
117                    for (MethodNode methodNode : methodNodes) {
118                            if (name.equals(methodNode.name) && desc.equals(methodNode.desc)) {
119                                    return methodNode;
120                            }
121                    }
122    
123                    return null;
124            }
125    
126            public static ClassNode loadAndRename(Class<?> clazz, String newName) {
127                    ClassLoader classLoader = clazz.getClassLoader();
128    
129                    String name = clazz.getName();
130    
131                    name = name.replace(CharPool.PERIOD, CharPool.SLASH);
132    
133                    ClassReader classReader = null;
134    
135                    try {
136                            classReader = new ClassReader(
137                                    classLoader.getResourceAsStream(name.concat(".class")));
138                    }
139                    catch (IOException ioe) {
140                            throw new RuntimeException(ioe);
141                    }
142    
143                    ClassNode classNode = new ClassNode();
144    
145                    ClassVisitor classVisitor = new RemappingClassAdapter(
146                            classNode,
147                            new RenameClassRemapper(name, newName)) {
148    
149                                    @Override
150                                    public void visitInnerClass(
151                                            String name, String outerName, String innerName,
152                                            int access) {
153                                    }
154    
155                            };
156    
157                    classReader.accept(
158                            classVisitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
159    
160                    return classNode;
161            }
162    
163            public static void mergeMethods(
164                    MethodNode containerMethodNode, MethodNode headMethodNode,
165                    MethodNode tailMethodNode) {
166    
167                    final MethodNode methodNode = new MethodNode();
168    
169                    headMethodNode.accept(
170                            new AdviceAdapter(
171                                    Opcodes.ASM5, methodNode, headMethodNode.access,
172                                    headMethodNode.name, headMethodNode.desc) {
173    
174                                            @Override
175                                            protected void onMethodExit(int opcode) {
176                                                    mv = _emptyMethodVisitor;
177                                            }
178    
179                                    });
180    
181                    tailMethodNode.accept(
182                            new AdviceAdapter(
183                                    Opcodes.ASM5, _emptyMethodVisitor, tailMethodNode.access,
184                                    tailMethodNode.name, tailMethodNode.desc) {
185    
186                                            @Override
187                                            protected void onMethodEnter() {
188                                                    mv = methodNode;
189                                            }
190    
191                                    });
192    
193                    containerMethodNode.instructions = methodNode.instructions;
194            }
195    
196            public static MethodNode removeMethodNode(
197                    List<MethodNode> methodNodes, String name, Type returnType,
198                    Type... argumentTypes) {
199    
200                    String desc = Type.getMethodDescriptor(returnType, argumentTypes);
201    
202                    for (MethodNode methodNode : methodNodes) {
203                            if (name.equals(methodNode.name) && desc.equals(methodNode.desc)) {
204                                    methodNodes.remove(methodNode);
205    
206                                    return methodNode;
207                            }
208                    }
209    
210                    return null;
211            }
212    
213            public static List<MethodNode> removeMethodNodes(
214                    List<MethodNode> methodNodes, int access) {
215    
216                    List<MethodNode> removedMethodNodes = new ArrayList<MethodNode>();
217    
218                    for (MethodNode methodNode : methodNodes) {
219                            if ((access & methodNode.access) != 0) {
220                                    removedMethodNodes.add(methodNode);
221                            }
222                    }
223    
224                    methodNodes.removeAll(removedMethodNodes);
225    
226                    return removedMethodNodes;
227            }
228    
229            public static List<MethodNode> removeMethodNodes(
230                    List<MethodNode> methodNodes, Set<String> annotations) {
231    
232                    List<MethodNode> removedMethodNodes = new ArrayList<MethodNode>();
233    
234                    for (MethodNode methodNode : methodNodes) {
235                            List<AnnotationNode> annotationNodes =
236                                    methodNode.visibleAnnotations;
237    
238                            if (annotationNodes != null) {
239                                    for (AnnotationNode annotationNode : annotationNodes) {
240                                            if (annotations.contains(annotationNode.desc)) {
241                                                    removedMethodNodes.add(methodNode);
242    
243                                                    break;
244                                            }
245                                    }
246                            }
247                    }
248    
249                    methodNodes.removeAll(removedMethodNodes);
250    
251                    return removedMethodNodes;
252            }
253    
254            public static List<MethodNode> removeMethodNodes(
255                    List<MethodNode> methodNodes, String name) {
256    
257                    List<MethodNode> removedMethodNodes = new ArrayList<MethodNode>();
258    
259                    for (MethodNode methodNode : methodNodes) {
260                            if (name.equals(methodNode.name)) {
261                                    removedMethodNodes.add(methodNode);
262                            }
263                    }
264    
265                    methodNodes.removeAll(removedMethodNodes);
266    
267                    return removedMethodNodes;
268            }
269    
270            private static final MethodVisitor _emptyMethodVisitor =
271                    new MethodVisitor(Opcodes.ASM5) {
272                    };
273    
274            private static class RenameClassRemapper extends Remapper {
275    
276                    public RenameClassRemapper(String oldClassName, String newClassName) {
277                            _oldClassName = oldClassName;
278                            _newClassName = newClassName;
279                    }
280    
281                    @Override
282                    public String map(String typeName) {
283                            if (typeName.equals(_oldClassName)) {
284                                    return _newClassName;
285                            }
286    
287                            return typeName;
288                    }
289    
290                    private final String _newClassName;
291                    private final String _oldClassName;
292    
293            }
294    
295    }