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 java.io.IOException; 23 import java.lang.reflect.Constructor; 24 import java.lang.reflect.Member; 25 import java.util.concurrent.ConcurrentHashMap; 26 import java.util.concurrent.ConcurrentMap; 27 28 /** 29 * Creates stack trace elements for members. 30 * 31 * @author crazybob@google.com (Bob Lee) 32 */ 33 public class StackTraceElements { 34 35 private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; 36 private static final InMemoryStackTraceElement[] EMPTY_INMEMORY_STACK_TRACE = 37 new InMemoryStackTraceElement[0]; 38 39 /*if[AOP]*/ 40 static final LoadingCache<Class<?>, LineNumbers> lineNumbersCache = 41 CacheBuilder.newBuilder() 42 .weakKeys() 43 .softValues() 44 .build( 45 new CacheLoader<Class<?>, LineNumbers>() { 46 @Override 47 public LineNumbers load(Class<?> key) { 48 try { 49 return new LineNumbers(key); 50 } catch (IOException e) { 51 throw new RuntimeException(e); 52 } 53 } 54 }); 55 /*end[AOP]*/ 56 57 private static final ConcurrentMap<InMemoryStackTraceElement, InMemoryStackTraceElement> 58 elementCache = new ConcurrentHashMap<>(); 59 private static final ConcurrentMap<String, String> stringCache = new ConcurrentHashMap<>(); 60 61 private static final String UNKNOWN_SOURCE = "Unknown Source"; 62 forMember(Member member)63 public static Object forMember(Member member) { 64 if (member == null) { 65 return SourceProvider.UNKNOWN_SOURCE; 66 } 67 68 Class declaringClass = member.getDeclaringClass(); 69 70 /*if[AOP]*/ 71 LineNumbers lineNumbers = lineNumbersCache.getUnchecked(declaringClass); 72 String fileName = lineNumbers.getSource(); 73 Integer lineNumberOrNull = lineNumbers.getLineNumber(member); 74 int lineNumber = lineNumberOrNull == null ? lineNumbers.getFirstLine() : lineNumberOrNull; 75 /*end[AOP]*/ 76 /*if[NO_AOP] 77 String fileName = null; 78 int lineNumber = -1; 79 end[NO_AOP]*/ 80 81 Class<? extends Member> memberType = Classes.memberType(member); 82 String memberName = memberType == Constructor.class ? "<init>" : member.getName(); 83 return new StackTraceElement(declaringClass.getName(), memberName, fileName, lineNumber); 84 } 85 forType(Class<?> implementation)86 public static Object forType(Class<?> implementation) { 87 /*if[AOP]*/ 88 LineNumbers lineNumbers = lineNumbersCache.getUnchecked(implementation); 89 int lineNumber = lineNumbers.getFirstLine(); 90 String fileName = lineNumbers.getSource(); 91 /*end[AOP]*/ 92 /*if[NO_AOP] 93 String fileName = null; 94 int lineNumber = -1; 95 end[NO_AOP]*/ 96 97 return new StackTraceElement(implementation.getName(), "class", fileName, lineNumber); 98 } 99 100 /** Clears the internal cache for {@link StackTraceElement StackTraceElements}. */ clearCache()101 public static void clearCache() { 102 elementCache.clear(); 103 stringCache.clear(); 104 } 105 106 /** Returns encoded in-memory version of {@link StackTraceElement StackTraceElements}. */ convertToInMemoryStackTraceElement( StackTraceElement[] stackTraceElements)107 public static InMemoryStackTraceElement[] convertToInMemoryStackTraceElement( 108 StackTraceElement[] stackTraceElements) { 109 if (stackTraceElements.length == 0) { 110 return EMPTY_INMEMORY_STACK_TRACE; 111 } 112 InMemoryStackTraceElement[] inMemoryStackTraceElements = 113 new InMemoryStackTraceElement[stackTraceElements.length]; 114 for (int i = 0; i < stackTraceElements.length; i++) { 115 inMemoryStackTraceElements[i] = 116 weakIntern(new InMemoryStackTraceElement(stackTraceElements[i])); 117 } 118 return inMemoryStackTraceElements; 119 } 120 121 /** 122 * Decodes in-memory stack trace elements to regular {@link StackTraceElement StackTraceElements}. 123 */ convertToStackTraceElement( InMemoryStackTraceElement[] inMemoryStackTraceElements)124 public static StackTraceElement[] convertToStackTraceElement( 125 InMemoryStackTraceElement[] inMemoryStackTraceElements) { 126 if (inMemoryStackTraceElements.length == 0) { 127 return EMPTY_STACK_TRACE; 128 } 129 StackTraceElement[] stackTraceElements = 130 new StackTraceElement[inMemoryStackTraceElements.length]; 131 for (int i = 0; i < inMemoryStackTraceElements.length; i++) { 132 String declaringClass = inMemoryStackTraceElements[i].getClassName(); 133 String methodName = inMemoryStackTraceElements[i].getMethodName(); 134 int lineNumber = inMemoryStackTraceElements[i].getLineNumber(); 135 stackTraceElements[i] = 136 new StackTraceElement(declaringClass, methodName, UNKNOWN_SOURCE, lineNumber); 137 } 138 return stackTraceElements; 139 } 140 weakIntern( InMemoryStackTraceElement inMemoryStackTraceElement)141 private static InMemoryStackTraceElement weakIntern( 142 InMemoryStackTraceElement inMemoryStackTraceElement) { 143 InMemoryStackTraceElement cached = elementCache.get(inMemoryStackTraceElement); 144 if (cached != null) { 145 return cached; 146 } 147 inMemoryStackTraceElement = 148 new InMemoryStackTraceElement( 149 weakIntern(inMemoryStackTraceElement.getClassName()), 150 weakIntern(inMemoryStackTraceElement.getMethodName()), 151 inMemoryStackTraceElement.getLineNumber()); 152 elementCache.put(inMemoryStackTraceElement, inMemoryStackTraceElement); 153 return inMemoryStackTraceElement; 154 } 155 weakIntern(String s)156 private static String weakIntern(String s) { 157 String cached = stringCache.get(s); 158 if (cached != null) { 159 return cached; 160 } 161 stringCache.put(s, s); 162 return s; 163 } 164 165 /** In-Memory version of {@link StackTraceElement} that does not store the file name. */ 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) 203 && e.lineNumber == lineNumber 204 && methodName.equals(e.methodName); 205 } 206 207 @Override hashCode()208 public int hashCode() { 209 int result = 31 * declaringClass.hashCode() + methodName.hashCode(); 210 result = 31 * result + lineNumber; 211 return result; 212 } 213 214 @Override toString()215 public String toString() { 216 return declaringClass + "." + methodName + "(" + lineNumber + ")"; 217 } 218 } 219 } 220