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 (at) 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 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 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 */ 98 public static void clearCache() { 99 cache.clear(); 100 } 101 102 /** 103 * Returns encoded in-memory version of {@link StackTraceElement StackTraceElements}. 104 */ 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 */ 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 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 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 171 InMemoryStackTraceElement(StackTraceElement ste) { 172 this(ste.getClassName(), ste.getMethodName(), ste.getLineNumber()); 173 } 174 175 InMemoryStackTraceElement(String declaringClass, String methodName, int lineNumber) { 176 this.declaringClass = declaringClass; 177 this.methodName = methodName; 178 this.lineNumber = lineNumber; 179 } 180 181 String getClassName() { 182 return declaringClass; 183 } 184 185 String getMethodName() { 186 return methodName; 187 } 188 189 int getLineNumber() { 190 return lineNumber; 191 } 192 193 @Override 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 207 public int hashCode() { 208 int result = 31 * declaringClass.hashCode() + methodName.hashCode(); 209 result = 31 * result + lineNumber; 210 return result; 211 } 212 213 @Override 214 public String toString() { 215 return declaringClass + "." + methodName + "(" + lineNumber + ")"; 216 } 217 } 218 } 219