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 */
020package org.sonar.java.bytecode.asm;
021
022import java.util.ArrayList;
023import java.util.List;
024
025import org.sonar.squid.api.SourceCodeEdgeUsage;
026
027public 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() == getParent() && edge.getUsage() == SourceCodeEdgeUsage.CALLS_FIELD && !((AsmField) edge.getTo()).isStatic();
202  }
203  
204  private boolean isCallToNonStaticInternalMethod(AsmEdge edge) {
205    return edge.getTargetAsmClass() == 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}