001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 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 }