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 }