001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2009 SonarSource SA
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.squid.bytecode;
021
022 import java.util.ArrayList;
023 import java.util.HashSet;
024 import java.util.List;
025 import java.util.Set;
026
027 import org.objectweb.asm.FieldVisitor;
028 import org.objectweb.asm.Label;
029 import org.objectweb.asm.MethodVisitor;
030 import org.objectweb.asm.Type;
031 import org.objectweb.asm.commons.EmptyVisitor;
032 import org.objectweb.asm.signature.SignatureReader;
033 import org.sonar.squid.api.SourceClass;
034 import org.sonar.squid.bytecode.asm.AsmSignatureVisitor;
035 import org.sonar.squid.indexer.SquidIndex;
036
037 public class AsmSquidBridge extends EmptyVisitor {
038
039 private List<AsmVisitor> visitors = new ArrayList<AsmVisitor>();
040 private SourceClass currentClass;
041 private final SquidIndex indexer;
042
043 public AsmSquidBridge(SquidIndex indexer, List<AsmVisitor> asmVisitors) {
044 this.indexer = indexer;
045 this.visitors = asmVisitors;
046 }
047
048 @Override
049 public void visit(final int version, final int access, final String className, final String signature, final String superClass,
050 final String[] interfaces) {
051 currentClass = searchSquidClass(className);
052 for (AsmVisitor visitor : visitors) {
053 visitor.visitClass(new AccessFlags(access), currentClass, searchSquidClass(superClass), searchSquidClasses(interfaces),
054 searchSquidClasses(analyzeSignature(signature)));
055 }
056 }
057
058 @Override
059 public FieldVisitor visitField(final int access, final String fieldName, final String fieldType, final String signature,
060 final Object value) {
061 for (AsmVisitor visitor : visitors) {
062 visitor.visitField(new AccessFlags(access), currentClass, fieldName, getSquidClassFromDescriptor(fieldType),
063 searchSquidClasses(analyzeSignature(signature)), analyzeFieldValue(value));
064 }
065 return this;
066 }
067
068 @Override
069 public MethodVisitor visitMethod(final int access, final String methodName, final String methodDesc, final String signature,
070 final String[] exceptions) {
071 for (AsmVisitor visitor : visitors) {
072 visitor.visitMethod(new AccessFlags(access), currentClass, methodName, getReturnSquidClass(methodDesc),
073 getArgumentSquidClasses(methodDesc), searchSquidClasses(analyzeSignature(signature)), searchSquidClasses(exceptions));
074 }
075 return this;
076 }
077
078 @Override
079 public void visitFieldInsn(final int opcode, final String ownerClass, final String name, final String fieldDescription) {
080 for (AsmVisitor visitor : visitors) {
081 visitor.visitOutsideFieldAccess(currentClass, searchSquidClass(ownerClass), getSquidClassFromDescriptor(fieldDescription));
082 }
083 }
084
085 @Override
086 public void visitTypeInsn(final int opcode, final String type) {
087 for (AsmVisitor visitor : visitors) {
088 visitor.visitTypeInsn(currentClass, searchSquidClass(type));
089 }
090 }
091
092 @Override
093 public void visitMethodInsn(final int opcode, final String ownerClass, final String methodName, final String methodDesc) {
094 for (AsmVisitor visitor : visitors) {
095 visitor.visitOutsideMethodAccess(currentClass, searchSquidClass(ownerClass), getArgumentSquidClasses(methodDesc));
096 }
097 }
098
099 @Override
100 public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String exception) {
101 for (AsmVisitor visitor : visitors) {
102 visitor.visitTryCatchBlock(currentClass, searchSquidClass(exception));
103 }
104 }
105
106 private Set<SourceClass> getArgumentSquidClasses(String desc) {
107 Set<SourceClass> resources = new HashSet<SourceClass>();
108 Type[] types = Type.getArgumentTypes(desc);
109 for (int i = 0; i < types.length; i++) {
110 resources.add(getSquidClassFromType(types[i]));
111 }
112 return resources;
113 }
114
115 private SourceClass getReturnSquidClass(String desc) {
116 return getSquidClassFromType(Type.getReturnType(desc));
117 }
118
119 private SourceClass analyzeFieldValue(Object value) {
120 if (value instanceof Type) {
121 return getSquidClassFromType((Type) value);
122 }
123 return null;
124 }
125
126 private String[] analyzeSignature(String signature) {
127 AsmSignatureVisitor signatureVisitor = new AsmSignatureVisitor();
128 if (signature != null) {
129 new SignatureReader(signature).accept(signatureVisitor);
130 }
131 return signatureVisitor.getInternalNames().toArray(new String[0]);
132 }
133
134 private SourceClass getSquidClassFromType(Type type) {
135 String className;
136 switch (type.getSort()) {
137 case Type.OBJECT:
138 className = type.getClassName();
139 break;
140 case Type.ARRAY:
141 className = type.getElementType().getClassName();
142 break;
143 default:
144 return null;
145 }
146 return (SourceClass) indexer.search(className);
147 }
148
149 private SourceClass getSquidClassFromDescriptor(String typeDescription) {
150 Type type = Type.getType(typeDescription);
151 return getSquidClassFromType(type);
152 }
153
154 private SourceClass searchSquidClass(String internalName) {
155 if (internalName == null) {
156 return null;
157 }
158 String classNameWithoutAnonymousInnerClassExtension = attachAnoInnerClassToParentClass(internalName);
159 return (SourceClass) indexer.search(Type.getObjectType(classNameWithoutAnonymousInnerClassExtension).getClassName());
160 }
161
162 private String attachAnoInnerClassToParentClass(String internalName) {
163 if (indexer.search(Type.getObjectType(internalName).getClassName()) == null && internalName.indexOf('$') != -1
164 && internalName.matches(".*\\d")) {
165 return internalName.substring(0, internalName.indexOf('$'));
166 }
167 return internalName;
168 }
169
170 private Set<SourceClass> searchSquidClasses(String[] internalNames) {
171 Set<SourceClass> resources = new HashSet<SourceClass>();
172 if (internalNames == null) {
173 return resources;
174 }
175 for (int i = 0; i < internalNames.length; i++) {
176 resources.add(searchSquidClass(internalNames[i]));
177 }
178 return resources;
179 }
180 }