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.java.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.java.bytecode.asm.AsmSignatureVisitor; 034 import org.sonar.squid.api.SourceClass; 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 Set<String> internalNames = signatureVisitor.getInternalNames(); 132 return internalNames.toArray(new String[internalNames.size()]); 133 } 134 135 private SourceClass getSquidClassFromType(Type type) { 136 String className; 137 switch (type.getSort()) { 138 case Type.OBJECT: 139 className = type.getInternalName(); 140 break; 141 case Type.ARRAY: 142 if (type.getElementType().getSort() == Type.OBJECT) { 143 className = type.getElementType().getInternalName(); 144 } else { 145 return null; 146 } 147 break; 148 default: 149 return null; 150 } 151 return (SourceClass) indexer.search(className); 152 } 153 154 private SourceClass getSquidClassFromDescriptor(String typeDescription) { 155 Type type = Type.getType(typeDescription); 156 return getSquidClassFromType(type); 157 } 158 159 private SourceClass searchSquidClass(String internalName) { 160 if (internalName == null) { 161 return null; 162 } 163 internalName = attachAnoInnerClassToParentClass(internalName); 164 return (SourceClass) indexer.search(internalName); 165 } 166 167 private String attachAnoInnerClassToParentClass(String internalName) { 168 if (indexer.search(internalName) == null && internalName.indexOf('$') != -1 && internalName.matches(".*\\d")) { 169 return internalName.substring(0, internalName.indexOf('$')); 170 } 171 return internalName; 172 } 173 174 private Set<SourceClass> searchSquidClasses(String[] internalNames) { 175 Set<SourceClass> resources = new HashSet<SourceClass>(); 176 if (internalNames == null) { 177 return resources; 178 } 179 for (int i = 0; i < internalNames.length; i++) { 180 resources.add(searchSquidClass(internalNames[i])); 181 } 182 return resources; 183 } 184 }