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, new RenameClassRemapper(name, newName)) {
146    
147                            @Override
148                            public void visitInnerClass(
149                                    String name, String outerName, String innerName, int access) {
150                            }
151    
152                    };
153    
154                    classReader.accept(
155                            classVisitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
156    
157                    return classNode;
158            }
159    
160            public static void mergeMethods(
161                    MethodNode containerMethodNode, MethodNode headMethodNode,
162                    MethodNode tailMethodNode) {
163    
164                    final MethodNode methodNode = new MethodNode();
165    
166                    headMethodNode.accept(
167                            new AdviceAdapter(
168                                    Opcodes.ASM5, methodNode, headMethodNode.access,
169                                    headMethodNode.name, headMethodNode.desc) {
170    
171                                    @Override
172                                    protected void onMethodExit(int opcode) {
173                                            mv = _emptyMethodVisitor;
174                                    }
175    
176                            });
177    
178                    tailMethodNode.accept(
179                            new AdviceAdapter(
180                                    Opcodes.ASM5, _emptyMethodVisitor, tailMethodNode.access,
181                                    tailMethodNode.name, tailMethodNode.desc) {
182    
183                                    @Override
184                                    protected void onMethodEnter() {
185                                            mv = methodNode;
186                                    }
187    
188                            });
189    
190                    containerMethodNode.instructions = methodNode.instructions;
191            }
192    
193            public static MethodNode removeMethodNode(
194                    List<MethodNode> methodNodes, String name, Type returnType,
195                    Type... argumentTypes) {
196    
197                    String desc = Type.getMethodDescriptor(returnType, argumentTypes);
198    
199                    for (MethodNode methodNode : methodNodes) {
200                            if (name.equals(methodNode.name) && desc.equals(methodNode.desc)) {
201                                    methodNodes.remove(methodNode);
202    
203                                    return methodNode;
204                            }
205                    }
206    
207                    return null;
208            }
209    
210            public static List<MethodNode> removeMethodNodes(
211                    List<MethodNode> methodNodes, int access) {
212    
213                    List<MethodNode> removedMethodNodes = new ArrayList<>();
214    
215                    for (MethodNode methodNode : methodNodes) {
216                            if ((access & methodNode.access) != 0) {
217                                    removedMethodNodes.add(methodNode);
218                            }
219                    }
220    
221                    methodNodes.removeAll(removedMethodNodes);
222    
223                    return removedMethodNodes;
224            }
225    
226            public static List<MethodNode> removeMethodNodes(
227                    List<MethodNode> methodNodes, Set<String> annotations) {
228    
229                    List<MethodNode> removedMethodNodes = new ArrayList<>();
230    
231                    for (MethodNode methodNode : methodNodes) {
232                            List<AnnotationNode> annotationNodes =
233                                    methodNode.visibleAnnotations;
234    
235                            if (annotationNodes != null) {
236                                    for (AnnotationNode annotationNode : annotationNodes) {
237                                            if (annotations.contains(annotationNode.desc)) {
238                                                    removedMethodNodes.add(methodNode);
239    
240                                                    break;
241                                            }
242                                    }
243                            }
244                    }
245    
246                    methodNodes.removeAll(removedMethodNodes);
247    
248                    return removedMethodNodes;
249            }
250    
251            public static List<MethodNode> removeMethodNodes(
252                    List<MethodNode> methodNodes, String name) {
253    
254                    List<MethodNode> removedMethodNodes = new ArrayList<>();
255    
256                    for (MethodNode methodNode : methodNodes) {
257                            if (name.equals(methodNode.name)) {
258                                    removedMethodNodes.add(methodNode);
259                            }
260                    }
261    
262                    methodNodes.removeAll(removedMethodNodes);
263    
264                    return removedMethodNodes;
265            }
266    
267            private static final MethodVisitor _emptyMethodVisitor =
268                    new MethodVisitor(Opcodes.ASM5) {
269                    };
270    
271            private static class RenameClassRemapper extends Remapper {
272    
273                    public RenameClassRemapper(String oldClassName, String newClassName) {
274                            _oldClassName = oldClassName;
275                            _newClassName = newClassName;
276                    }
277    
278                    @Override
279                    public String map(String typeName) {
280                            if (typeName.equals(_oldClassName)) {
281                                    return _newClassName;
282                            }
283    
284                            return typeName;
285                    }
286    
287                    private final String _newClassName;
288                    private final String _oldClassName;
289    
290            }
291    
292    }