• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.internal.util;
18 
19 import com.google.common.cache.CacheBuilder;
20 import com.google.common.cache.CacheLoader;
21 import com.google.common.cache.LoadingCache;
22 import com.google.common.collect.MapMaker;
23 
24 import java.io.IOException;
25 import java.lang.reflect.Constructor;
26 import java.lang.reflect.Member;
27 import java.util.Map;
28 
29 /**
30  * Creates stack trace elements for members.
31  *
32  * @author crazybob@google.com (Bob Lee)
33  */
34 public class StackTraceElements {
35 
36   private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
37   private static final InMemoryStackTraceElement[] EMPTY_INMEMORY_STACK_TRACE =
38       new InMemoryStackTraceElement[0];
39 
40   /*if[AOP]*/
41   static final LoadingCache<Class<?>, LineNumbers> lineNumbersCache =
42       CacheBuilder.newBuilder().weakKeys().softValues().build(
43           new CacheLoader<Class<?>, LineNumbers>() {
44             public LineNumbers load(Class<?> key) {
45               try {
46                 return new LineNumbers(key);
47               }
48               catch (IOException e) {
49                 throw new RuntimeException(e);
50               }
51             }
52           });
53   /*end[AOP]*/
54 
55   private static Map<Object, Object> cache = new MapMaker().makeMap();
56   private static final String UNKNOWN_SOURCE = "Unknown Source";
57 
forMember(Member member)58   public static Object forMember(Member member) {
59     if (member == null) {
60       return SourceProvider.UNKNOWN_SOURCE;
61     }
62 
63     Class declaringClass = member.getDeclaringClass();
64 
65     /*if[AOP]*/
66     LineNumbers lineNumbers = lineNumbersCache.getUnchecked(declaringClass);
67     String fileName = lineNumbers.getSource();
68     Integer lineNumberOrNull = lineNumbers.getLineNumber(member);
69     int lineNumber = lineNumberOrNull == null ? lineNumbers.getFirstLine() : lineNumberOrNull;
70     /*end[AOP]*/
71     /*if[NO_AOP]
72     String fileName = null;
73     int lineNumber = -1;
74     end[NO_AOP]*/
75 
76     Class<? extends Member> memberType = Classes.memberType(member);
77     String memberName = memberType == Constructor.class ? "<init>" : member.getName();
78     return new StackTraceElement(declaringClass.getName(), memberName, fileName, lineNumber);
79   }
80 
forType(Class<?> implementation)81   public static Object forType(Class<?> implementation) {
82     /*if[AOP]*/
83     LineNumbers lineNumbers = lineNumbersCache.getUnchecked(implementation);
84     int lineNumber = lineNumbers.getFirstLine();
85     String fileName = lineNumbers.getSource();
86     /*end[AOP]*/
87     /*if[NO_AOP]
88     String fileName = null;
89     int lineNumber = -1;
90     end[NO_AOP]*/
91 
92     return new StackTraceElement(implementation.getName(), "class", fileName, lineNumber);
93   }
94 
95   /**
96    * Clears the internal cache for {@link StackTraceElement StackTraceElements}.
97    */
clearCache()98   public static void clearCache() {
99     cache.clear();
100   }
101 
102   /**
103    * Returns encoded in-memory version of {@link StackTraceElement StackTraceElements}.
104    */
convertToInMemoryStackTraceElement( StackTraceElement[] stackTraceElements)105   public static InMemoryStackTraceElement[] convertToInMemoryStackTraceElement(
106       StackTraceElement[] stackTraceElements) {
107     if (stackTraceElements.length == 0) {
108       return EMPTY_INMEMORY_STACK_TRACE;
109     }
110     InMemoryStackTraceElement[] inMemoryStackTraceElements =
111         new InMemoryStackTraceElement[stackTraceElements.length];
112     for (int i = 0; i < stackTraceElements.length; i++) {
113       inMemoryStackTraceElements[i] =
114           weakIntern(new InMemoryStackTraceElement(stackTraceElements[i]));
115     }
116     return inMemoryStackTraceElements;
117   }
118 
119   /**
120    * Decodes in-memory stack trace elements to regular {@link StackTraceElement StackTraceElements}.
121    */
convertToStackTraceElement( InMemoryStackTraceElement[] inMemoryStackTraceElements)122   public static StackTraceElement[] convertToStackTraceElement(
123       InMemoryStackTraceElement[] inMemoryStackTraceElements) {
124     if (inMemoryStackTraceElements.length == 0) {
125       return EMPTY_STACK_TRACE;
126     }
127     StackTraceElement[] stackTraceElements =
128         new StackTraceElement[inMemoryStackTraceElements.length];
129     for (int i = 0; i < inMemoryStackTraceElements.length; i++) {
130       String declaringClass = inMemoryStackTraceElements[i].getClassName();
131       String methodName = inMemoryStackTraceElements[i].getMethodName();
132       int lineNumber = inMemoryStackTraceElements[i].getLineNumber();
133       stackTraceElements[i] =
134           new StackTraceElement(declaringClass, methodName, UNKNOWN_SOURCE, lineNumber);
135     }
136     return stackTraceElements;
137   }
138 
weakIntern( InMemoryStackTraceElement inMemoryStackTraceElement)139   private static InMemoryStackTraceElement weakIntern(
140       InMemoryStackTraceElement inMemoryStackTraceElement) {
141     InMemoryStackTraceElement cached =
142         (InMemoryStackTraceElement) cache.get(inMemoryStackTraceElement);
143     if (cached != null) {
144       return cached;
145     }
146     inMemoryStackTraceElement = new InMemoryStackTraceElement(
147         weakIntern(inMemoryStackTraceElement.getClassName()),
148         weakIntern(inMemoryStackTraceElement.getMethodName()),
149         inMemoryStackTraceElement.getLineNumber());
150     cache.put(inMemoryStackTraceElement, inMemoryStackTraceElement);
151     return inMemoryStackTraceElement;
152   }
153 
weakIntern(String s)154   private static String weakIntern(String s) {
155     String cached = (String) cache.get(s);
156     if (cached != null) {
157       return cached;
158     }
159     cache.put(s, s);
160     return s;
161   }
162 
163   /**
164    * In-Memory version of {@link StackTraceElement} that does not store the file name.
165    */
166   public static class InMemoryStackTraceElement {
167     private String declaringClass;
168     private String methodName;
169     private int lineNumber;
170 
InMemoryStackTraceElement(StackTraceElement ste)171     InMemoryStackTraceElement(StackTraceElement ste) {
172       this(ste.getClassName(), ste.getMethodName(), ste.getLineNumber());
173     }
174 
InMemoryStackTraceElement(String declaringClass, String methodName, int lineNumber)175     InMemoryStackTraceElement(String declaringClass, String methodName, int lineNumber) {
176       this.declaringClass = declaringClass;
177       this.methodName = methodName;
178       this.lineNumber = lineNumber;
179     }
180 
getClassName()181     String getClassName() {
182       return declaringClass;
183     }
184 
getMethodName()185     String getMethodName() {
186       return methodName;
187     }
188 
getLineNumber()189     int getLineNumber() {
190       return lineNumber;
191     }
192 
193     @Override
equals(Object obj)194     public boolean equals(Object obj) {
195       if (obj == this) {
196         return true;
197       }
198       if (!(obj instanceof InMemoryStackTraceElement)) {
199         return false;
200       }
201       InMemoryStackTraceElement e = (InMemoryStackTraceElement) obj;
202       return e.declaringClass.equals(declaringClass) && e.lineNumber == lineNumber &&
203           methodName.equals(e.methodName);
204     }
205 
206     @Override
hashCode()207     public int hashCode() {
208       int result = 31 * declaringClass.hashCode() + methodName.hashCode();
209       result = 31 * result + lineNumber;
210       return result;
211     }
212 
213     @Override
toString()214     public String toString() {
215       return declaringClass + "." + methodName + "(" + lineNumber + ")";
216     }
217   }
218 }
219