001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 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.asm; 021 022 import java.util.ArrayList; 023 import java.util.List; 024 025 import org.sonar.squid.api.SourceCodeEdgeUsage; 026 027 public class AsmMethod extends AsmResource { 028 029 private String name; 030 private String key; 031 private boolean inherited = false; 032 private boolean empty = false; 033 private boolean bodyLoaded = true; 034 private boolean accessedFieldComputed = false; 035 private boolean accessedFieldBeingComputed = false; 036 private boolean accessedFieldIsThisMethodRecursive = false; 037 private AsmField accessedField = null; 038 private String signature; 039 private AsmMethod implementationLinkage = null; 040 041 public AsmMethod(AsmClass parent, String name, String descriptor) { 042 this.parent = parent; 043 this.name = name; 044 key = name + descriptor; 045 } 046 047 public AsmMethod(AsmClass parent, String key) { 048 this.parent = parent; 049 this.key = key; 050 this.name = key.substring(0, key.indexOf('(')); 051 } 052 053 public String getName() { 054 return name; 055 } 056 057 public String getKey() { 058 return key; 059 } 060 061 public String getGenericKey() { 062 if (signature != null) { 063 return name + signature; 064 } 065 return getKey(); 066 } 067 068 public void setSignature(String signature) { 069 this.signature = signature; 070 } 071 072 public String getSignature() { 073 return signature; 074 } 075 076 public List<AsmField> getCallsToField() { 077 List<AsmField> callsToField = new ArrayList<AsmField>(); 078 for (AsmEdge usage : getOutgoingEdges()) { 079 if (usage.getUsage() == SourceCodeEdgeUsage.CALLS_FIELD) { 080 callsToField.add((AsmField) usage.getTo()); 081 } 082 } 083 return callsToField; 084 } 085 086 public List<AsmMethod> getCallsToMethod() { 087 List<AsmMethod> callsToMethod = new ArrayList<AsmMethod>(); 088 for (AsmEdge usage : getOutgoingEdges()) { 089 if (usage.getUsage() == SourceCodeEdgeUsage.CALLS_METHOD) { 090 callsToMethod.add((AsmMethod) usage.getTo()); 091 } 092 } 093 return callsToMethod; 094 } 095 096 @Override 097 public boolean equals(Object object) { 098 if (this == object) { 099 return true; 100 } 101 if (object instanceof AsmMethod) { 102 AsmMethod otherMethod = (AsmMethod) object; 103 return parent.equals(otherMethod.parent) && key.equals(otherMethod.key); 104 } 105 return false; 106 } 107 108 @Override 109 public int hashCode() { 110 return parent.hashCode() + key.hashCode(); 111 } 112 113 public boolean isConstructor() { 114 return "<init>".equals(name) || "<clinit>".equals(name); 115 } 116 117 public boolean isDefaultConstructor() { 118 return "<init>()V".equals(key); 119 } 120 121 void setInherited(boolean inherited) { 122 this.inherited = inherited; 123 } 124 125 public boolean isInherited() { 126 return inherited; 127 } 128 129 public boolean isEmpty() { 130 return empty; 131 } 132 133 public boolean isBodyLoaded() { 134 return bodyLoaded; 135 } 136 137 void setBodyLoaded(boolean bodyLoaded) { 138 this.bodyLoaded = bodyLoaded; 139 } 140 141 void setEmpty(boolean empty) { 142 this.empty = empty; 143 } 144 145 public boolean isAccessor() { 146 return getAccessedField() != null; 147 } 148 149 public AsmField getAccessedField() { 150 if (accessedFieldComputed) { 151 return accessedField; 152 } 153 if (accessedFieldBeingComputed) { 154 // Do not set accessedField here, because the pending computeAccessedField() will overwrite it anyway 155 accessedFieldIsThisMethodRecursive = true; 156 return null; 157 } else { 158 accessedFieldBeingComputed = true; // Prevents infinite recursion on recursive methods. 159 computeAccessedField(); 160 if (accessedFieldIsThisMethodRecursive) { 161 // We already returned null previously during the computation, so we must return null for consistency 162 accessedField = null; 163 } 164 accessedFieldComputed = true; 165 accessedFieldBeingComputed = false; 166 167 return accessedField; 168 } 169 } 170 171 private void computeAccessedField() { 172 if (!isConstructor()) { 173 for (AsmEdge edge: getOutgoingEdges()) { 174 if (isCallToNonStaticInternalField(edge)) { 175 if (isFieldAccesingDifferentField((AsmField)edge.getTo())) { 176 accessedField = null; 177 break; 178 } 179 accessedField = (AsmField)edge.getTo(); 180 } else if (isCallToNonStaticInternalMethod(edge)) { 181 AsmMethod method = (AsmMethod)edge.getTo(); 182 if (isMethodNotAccessorOrAccessingDifferentField(method)) { 183 accessedField = null; 184 break; 185 } 186 accessedField = method.getAccessedField(); 187 } 188 } 189 } 190 } 191 192 private boolean isMethodNotAccessorOrAccessingDifferentField(AsmMethod method) { 193 return !method.isAccessor() || (accessedField != null && !accessedField.equals(method.getAccessedField())); 194 } 195 196 private boolean isFieldAccesingDifferentField(AsmField field) { 197 return accessedField != null && accessedField != field; 198 } 199 200 private boolean isCallToNonStaticInternalField(AsmEdge edge) { 201 return edge.getTargetAsmClass() == (AsmClass)getParent() && edge.getUsage() == SourceCodeEdgeUsage.CALLS_FIELD && !((AsmField)edge.getTo()).isStatic(); 202 } 203 204 private boolean isCallToNonStaticInternalMethod(AsmEdge edge) { 205 return edge.getTargetAsmClass() == (AsmClass)getParent() && edge.getUsage() == SourceCodeEdgeUsage.CALLS_METHOD && !((AsmMethod)edge.getTo()).isStatic(); 206 } 207 208 @Override 209 public String toString() { 210 return key; 211 } 212 213 public boolean isStaticConstructor() { 214 return "<init>".equals(name); 215 } 216 217 public void linkTo(AsmMethod implementationLinkage) { 218 this.implementationLinkage = implementationLinkage; 219 } 220 221 public AsmMethod getImplementationLinkage() { 222 return implementationLinkage; 223 } 224 }