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) || (sort == Type.SHORT)) {
050    
051                            methodVisitor.visitInsn(Opcodes.ICONST_0);
052                            methodVisitor.visitInsn(Opcodes.IRETURN);
053                    }
054                    else if (sort == Type.DOUBLE) {
055                            methodVisitor.visitInsn(Opcodes.DCONST_0);
056                            methodVisitor.visitInsn(Opcodes.DRETURN);
057                    }
058                    else if (sort == Type.FLOAT) {
059                            methodVisitor.visitInsn(Opcodes.FCONST_0);
060                            methodVisitor.visitInsn(Opcodes.FRETURN);
061                    }
062                    else if (sort == Type.LONG) {
063                            methodVisitor.visitInsn(Opcodes.LCONST_0);
064                            methodVisitor.visitInsn(Opcodes.LRETURN);
065                    }
066                    else if (sort == Type.VOID) {
067                            methodVisitor.visitInsn(Opcodes.RETURN);
068                    }
069                    else {
070                            methodVisitor.visitInsn(Opcodes.ACONST_NULL);
071                            methodVisitor.visitInsn(Opcodes.ARETURN);
072                    }
073            }
074    
075            public static List<FieldNode> addFieldNodes(
076                    List<FieldNode> fieldNodes, List<FieldNode> newFieldNodes) {
077    
078                    List<FieldNode> addedFieldNodes = new ArrayList<>();
079    
080                    newFieldNode:
081                    for (FieldNode newFieldNode : newFieldNodes) {
082                            String newFieldNodeName = newFieldNode.name;
083    
084                            for (FieldNode fieldNode : fieldNodes) {
085                                    if (newFieldNodeName.equals(fieldNode.name)) {
086                                            continue newFieldNode;
087                                    }
088                            }
089    
090                            addedFieldNodes.add(newFieldNode);
091                    }
092    
093                    fieldNodes.addAll(addedFieldNodes);
094    
095                    return addedFieldNodes;
096            }
097    
098            public static FieldNode findFieldNode(
099                    List<FieldNode> fieldNodes, String name) {
100    
101                    for (FieldNode fieldNode : fieldNodes) {
102                            if (name.equals(fieldNode.name)) {
103                                    return fieldNode;
104                            }
105                    }
106    
107                    return null;
108            }
109    
110            public static MethodNode findMethodNode(
111                    List<MethodNode> methodNodes, String name, Type returnType,
112                    Type... argumentTypes) {
113    
114                    String desc = Type.getMethodDescriptor(returnType, argumentTypes);
115    
116                    for (MethodNode methodNode : methodNodes) {
117                            if (name.equals(methodNode.name) && desc.equals(methodNode.desc)) {
118                                    return methodNode;
119                            }
120                    }
121    
122                    return null;
123            }
124    
125            public static ClassNode loadAndRename(Class<?> clazz, String newName) {
126                    ClassLoader classLoader = clazz.getClassLoader();
127    
128                    String name = clazz.getName();
129    
130                    name = name.replace(CharPool.PERIOD, CharPool.SLASH);
131    
132                    ClassReader classReader = null;
133    
134                    try {
135                            classReader = new ClassReader(
136                                    classLoader.getResourceAsStream(name.concat(".class")));
137                    }
138                    catch (IOException ioe) {
139                            throw new RuntimeException(ioe);
140                    }
141    
142                    ClassNode classNode = new ClassNode();
143    
144                    ClassVisitor classVisitor = new RemappingClassAdapter(
145                            classNode,
146                            new RenameClassRemapper(name, newName)) {
147    
148                                    @Override
149                                    public void visitInnerClass(
150                                            String name, String outerName, String innerName,
151                                            int access) {
152                                    }
153    
154                            };
155    
156                    classReader.accept(
157                            classVisitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
158    
159                    return classNode;
160            }
161    
162            public static void mergeMethods(
163                    MethodNode containerMethodNode, MethodNode headMethodNode,
164                    MethodNode tailMethodNode) {
165    
166                    final MethodNode methodNode = new MethodNode();
167    
168                    headMethodNode.accept(
169                            new AdviceAdapter(
170                                    Opcodes.ASM5, methodNode, headMethodNode.access,
171                                    headMethodNode.name, headMethodNode.desc) {
172    
173                                            @Override
174                                            protected void onMethodExit(int opcode) {
175                                                    mv = _emptyMethodVisitor;
176                                            }
177    
178                                    });
179    
180                    tailMethodNode.accept(
181                            new AdviceAdapter(
182                                    Opcodes.ASM5, _emptyMethodVisitor, tailMethodNode.access,
183                                    tailMethodNode.name, tailMethodNode.desc) {
184    
185                                            @Override
186                                            protected void onMethodEnter() {
187                                                    mv = methodNode;
188                                            }
189    
190                                    });
191    
192                    containerMethodNode.instructions = methodNode.instructions;
193            }
194    
195            public static MethodNode removeMethodNode(
196                    List<MethodNode> methodNodes, String name, Type returnType,
197                    Type... argumentTypes) {
198    
199                    String desc = Type.getMethodDescriptor(returnType, argumentTypes);
200    
201                    for (MethodNode methodNode : methodNodes) {
202                            if (name.equals(methodNode.name) && desc.equals(methodNode.desc)) {
203                                    methodNodes.remove(methodNode);
204    
205                                    return methodNode;
206                            }
207                    }
208    
209                    return null;
210            }
211    
212            public static List<MethodNode> removeMethodNodes(
213                    List<MethodNode> methodNodes, int access) {
214    
215                    List<MethodNode> removedMethodNodes = new ArrayList<>();
216    
217                    for (MethodNode methodNode : methodNodes) {
218                            if ((access & methodNode.access) != 0) {
219                                    removedMethodNodes.add(methodNode);
220                            }
221                    }
222    
223                    methodNodes.removeAll(removedMethodNodes);
224    
225                    return removedMethodNodes;
226            }
227    
228            public static List<MethodNode> removeMethodNodes(
229                    List<MethodNode> methodNodes, Set<String> annotations) {
230    
231                    List<MethodNode> removedMethodNodes = new ArrayList<>();
232    
233                    for (MethodNode methodNode : methodNodes) {
234                            List<AnnotationNode> annotationNodes =
235                                    methodNode.visibleAnnotations;
236    
237                            if (annotationNodes != null) {
238                                    for (AnnotationNode annotationNode : annotationNodes) {
239                                            if (annotations.contains(annotationNode.desc)) {
240                                                    removedMethodNodes.add(methodNode);
241    
242                                                    break;
243                                            }
244                                    }
245                            }
246                    }
247    
248                    methodNodes.removeAll(removedMethodNodes);
249    
250                    return removedMethodNodes;
251            }
252    
253            public static List<MethodNode> removeMethodNodes(
254                    List<MethodNode> methodNodes, String name) {
255    
256                    List<MethodNode> removedMethodNodes = new ArrayList<>();
257    
258                    for (MethodNode methodNode : methodNodes) {
259                            if (name.equals(methodNode.name)) {
260                                    removedMethodNodes.add(methodNode);
261                            }
262                    }
263    
264                    methodNodes.removeAll(removedMethodNodes);
265    
266                    return removedMethodNodes;
267            }
268    
269            private static final MethodVisitor _emptyMethodVisitor =
270                    new MethodVisitor(Opcodes.ASM5) {
271                    };
272    
273            private static class RenameClassRemapper extends Remapper {
274    
275                    public RenameClassRemapper(String oldClassName, String newClassName) {
276                            _oldClassName = oldClassName;
277                            _newClassName = newClassName;
278                    }
279    
280                    @Override
281                    public String map(String typeName) {
282                            if (typeName.equals(_oldClassName)) {
283                                    return _newClassName;
284                            }
285    
286                            return typeName;
287                    }
288    
289                    private final String _newClassName;
290                    private final String _oldClassName;
291    
292            }
293    
294    }