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    }