001// ASM: a very small and fast Java bytecode manipulation framework 002// Copyright (c) 2000-2011 INRIA, France Telecom 003// All rights reserved. 004// 005// Redistribution and use in source and binary forms, with or without 006// modification, are permitted provided that the following conditions 007// are met: 008// 1. Redistributions of source code must retain the above copyright 009// notice, this list of conditions and the following disclaimer. 010// 2. Redistributions in binary form must reproduce the above copyright 011// notice, this list of conditions and the following disclaimer in the 012// documentation and/or other materials provided with the distribution. 013// 3. Neither the name of the copyright holders nor the names of its 014// contributors may be used to endorse or promote products derived from 015// this software without specific prior written permission. 016// 017// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 018// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 019// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 020// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 021// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 022// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 023// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 024// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 025// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 026// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 027// THE POSSIBILITY OF SUCH DAMAGE. 028 029package io.ebean.enhance.asm.commons; 030 031import io.ebean.enhance.asm.ConstantDynamic; 032import io.ebean.enhance.asm.Handle; 033import io.ebean.enhance.asm.Opcodes; 034import io.ebean.enhance.asm.Type; 035import io.ebean.enhance.asm.signature.SignatureReader; 036import io.ebean.enhance.asm.signature.SignatureVisitor; 037import io.ebean.enhance.asm.signature.SignatureWriter; 038 039/** 040 * A class responsible for remapping types and names. 041 * 042 * @author Eugene Kuleshov 043 */ 044public abstract class Remapper { 045 046 /** 047 * Returns the given descriptor, remapped with {@link #map(String)}. 048 * 049 * @param descriptor a type descriptor. 050 * @return the given descriptor, with its [array element type] internal name remapped with {@link 051 * #map(String)} (if the descriptor corresponds to an array or object type, otherwise the 052 * descriptor is returned as is). 053 */ 054 public String mapDesc(final String descriptor) { 055 return mapType(Type.getType(descriptor)).getDescriptor(); 056 } 057 058 /** 059 * Returns the given {@link Type}, remapped with {@link #map(String)} or {@link 060 * #mapMethodDesc(String)}. 061 * 062 * @param type a type, which can be a method type. 063 * @return the given type, with its [array element type] internal name remapped with {@link 064 * #map(String)} (if the type is an array or object type, otherwise the type is returned as 065 * is) or, of the type is a method type, with its descriptor remapped with {@link 066 * #mapMethodDesc(String)}. 067 */ 068 private Type mapType(final Type type) { 069 switch (type.getSort()) { 070 case Type.ARRAY: 071 StringBuilder remappedDescriptor = new StringBuilder(); 072 for (int i = 0; i < type.getDimensions(); ++i) { 073 remappedDescriptor.append('['); 074 } 075 remappedDescriptor.append(mapType(type.getElementType()).getDescriptor()); 076 return Type.getType(remappedDescriptor.toString()); 077 case Type.OBJECT: 078 String remappedInternalName = map(type.getInternalName()); 079 return remappedInternalName != null ? Type.getObjectType(remappedInternalName) : type; 080 case Type.METHOD: 081 return Type.getMethodType(mapMethodDesc(type.getDescriptor())); 082 default: 083 return type; 084 } 085 } 086 087 /** 088 * Returns the given internal name, remapped with {@link #map(String)}. 089 * 090 * @param internalName the internal name (or array type descriptor) of some (array) class. 091 * @return the given internal name, remapped with {@link #map(String)}. 092 */ 093 public String mapType(final String internalName) { 094 if (internalName == null) { 095 return null; 096 } 097 return mapType(Type.getObjectType(internalName)).getInternalName(); 098 } 099 100 /** 101 * Returns the given internal names, remapped with {@link #map(String)}. 102 * 103 * @param internalNames the internal names (or array type descriptors) of some (array) classes. 104 * @return the given internal name, remapped with {@link #map(String)}. 105 */ 106 public String[] mapTypes(final String[] internalNames) { 107 String[] remappedInternalNames = null; 108 for (int i = 0; i < internalNames.length; ++i) { 109 String internalName = internalNames[i]; 110 String remappedInternalName = mapType(internalName); 111 if (remappedInternalName != null) { 112 if (remappedInternalNames == null) { 113 remappedInternalNames = new String[internalNames.length]; 114 System.arraycopy(internalNames, 0, remappedInternalNames, 0, internalNames.length); 115 } 116 remappedInternalNames[i] = remappedInternalName; 117 } 118 } 119 return remappedInternalNames != null ? remappedInternalNames : internalNames; 120 } 121 122 /** 123 * Returns the given method descriptor, with its argument and return type descriptors remapped 124 * with {@link #mapDesc(String)}. 125 * 126 * @param methodDescriptor a method descriptor. 127 * @return the given method descriptor, with its argument and return type descriptors remapped 128 * with {@link #mapDesc(String)}. 129 */ 130 public String mapMethodDesc(final String methodDescriptor) { 131 if ("()V".equals(methodDescriptor)) { 132 return methodDescriptor; 133 } 134 135 StringBuilder stringBuilder = new StringBuilder("("); 136 for (Type argumentType : Type.getArgumentTypes(methodDescriptor)) { 137 stringBuilder.append(mapType(argumentType).getDescriptor()); 138 } 139 Type returnType = Type.getReturnType(methodDescriptor); 140 if (returnType == Type.VOID_TYPE) { 141 stringBuilder.append(")V"); 142 } else { 143 stringBuilder.append(')').append(mapType(returnType).getDescriptor()); 144 } 145 return stringBuilder.toString(); 146 } 147 148 /** 149 * Returns the given value, remapped with this remapper. Possible values are {@link Boolean}, 150 * {@link Byte}, {@link Short}, {@link Character}, {@link Integer}, {@link Long}, {@link Double}, 151 * {@link Float}, {@link String}, {@link Type}, {@link Handle}, {@link ConstantDynamic} or arrays 152 * of primitive types . 153 * 154 * @param value an object. Only {@link Type}, {@link Handle} and {@link ConstantDynamic} values 155 * are remapped. 156 * @return the given value, remapped with this remapper. 157 */ 158 public Object mapValue(final Object value) { 159 if (value instanceof Type) { 160 return mapType((Type) value); 161 } 162 if (value instanceof Handle) { 163 Handle handle = (Handle) value; 164 return new Handle( 165 handle.getTag(), 166 mapType(handle.getOwner()), 167 mapMethodName(handle.getOwner(), handle.getName(), handle.getDesc()), 168 handle.getTag() <= Opcodes.H_PUTSTATIC 169 ? mapDesc(handle.getDesc()) 170 : mapMethodDesc(handle.getDesc()), 171 handle.isInterface()); 172 } 173 if (value instanceof ConstantDynamic) { 174 ConstantDynamic constantDynamic = (ConstantDynamic) value; 175 int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount(); 176 Object[] remappedBootstrapMethodArguments = new Object[bootstrapMethodArgumentCount]; 177 for (int i = 0; i < bootstrapMethodArgumentCount; ++i) { 178 remappedBootstrapMethodArguments[i] = 179 mapValue(constantDynamic.getBootstrapMethodArgument(i)); 180 } 181 String descriptor = constantDynamic.getDescriptor(); 182 return new ConstantDynamic( 183 mapInvokeDynamicMethodName(constantDynamic.getName(), descriptor), 184 mapDesc(descriptor), 185 (Handle) mapValue(constantDynamic.getBootstrapMethod()), 186 remappedBootstrapMethodArguments); 187 } 188 return value; 189 } 190 191 /** 192 * Returns the given signature, remapped with the {@link SignatureVisitor} returned by {@link 193 * #createSignatureRemapper(SignatureVisitor)}. 194 * 195 * @param signature a <i>JavaTypeSignature</i>, <i>ClassSignature</i> or <i>MethodSignature</i>. 196 * @param typeSignature whether the given signature is a <i>JavaTypeSignature</i>. 197 * @return signature the given signature, remapped with the {@link SignatureVisitor} returned by 198 * {@link #createSignatureRemapper(SignatureVisitor)}. 199 */ 200 public String mapSignature(final String signature, final boolean typeSignature) { 201 if (signature == null) { 202 return null; 203 } 204 SignatureReader signatureReader = new SignatureReader(signature); 205 SignatureWriter signatureWriter = new SignatureWriter(); 206 SignatureVisitor signatureRemapper = createSignatureRemapper(signatureWriter); 207 if (typeSignature) { 208 signatureReader.acceptType(signatureRemapper); 209 } else { 210 signatureReader.accept(signatureRemapper); 211 } 212 return signatureWriter.toString(); 213 } 214 215 /** 216 * Constructs a new remapper for signatures. The default implementation of this method returns a 217 * new {@link SignatureRemapper}. 218 * 219 * @param signatureVisitor the SignatureVisitor the remapper must delegate to. 220 * @return the newly created remapper. 221 * @deprecated use {@link #createSignatureRemapper} instead. 222 */ 223 @Deprecated 224 protected SignatureVisitor createRemappingSignatureAdapter( 225 final SignatureVisitor signatureVisitor) { 226 return createSignatureRemapper(signatureVisitor); 227 } 228 229 /** 230 * Constructs a new remapper for signatures. The default implementation of this method returns a 231 * new {@link SignatureRemapper}. 232 * 233 * @param signatureVisitor the SignatureVisitor the remapper must delegate to. 234 * @return the newly created remapper. 235 */ 236 protected SignatureVisitor createSignatureRemapper(final SignatureVisitor signatureVisitor) { 237 return new SignatureRemapper(signatureVisitor, this); 238 } 239 240 /** 241 * Maps an inner class name to its new name. The default implementation of this method provides a 242 * strategy that will work for inner classes produced by Java, but not necessarily other 243 * languages. Subclasses can override. 244 * 245 * @param name the fully-qualified internal name of the inner class. 246 * @param ownerName the internal name of the owner class of the inner class. 247 * @param innerName the internal name of the inner class. 248 * @return the new inner name of the inner class. 249 */ 250 public String mapInnerClassName( 251 final String name, final String ownerName, final String innerName) { 252 final String remappedInnerName = this.mapType(name); 253 if (remappedInnerName.contains("$")) { 254 int index = remappedInnerName.lastIndexOf('$') + 1; 255 while (index < remappedInnerName.length() 256 && Character.isDigit(remappedInnerName.charAt(index))) { 257 index++; 258 } 259 return remappedInnerName.substring(index); 260 } else { 261 return innerName; 262 } 263 } 264 265 /** 266 * Maps a method name to its new name. The default implementation of this method returns the given 267 * name, unchanged. Subclasses can override. 268 * 269 * @param owner the internal name of the owner class of the method. 270 * @param name the name of the method. 271 * @param descriptor the descriptor of the method. 272 * @return the new name of the method. 273 */ 274 public String mapMethodName(final String owner, final String name, final String descriptor) { 275 return name; 276 } 277 278 /** 279 * Maps an invokedynamic or a constant dynamic method name to its new name. The default 280 * implementation of this method returns the given name, unchanged. Subclasses can override. 281 * 282 * @param name the name of the method. 283 * @param descriptor the descriptor of the method. 284 * @return the new name of the method. 285 */ 286 public String mapInvokeDynamicMethodName(final String name, final String descriptor) { 287 return name; 288 } 289 290 /** 291 * Maps a field name to its new name. The default implementation of this method returns the given 292 * name, unchanged. Subclasses can override. 293 * 294 * @param owner the internal name of the owner class of the field. 295 * @param name the name of the field. 296 * @param descriptor the descriptor of the field. 297 * @return the new name of the field. 298 */ 299 public String mapFieldName(final String owner, final String name, final String descriptor) { 300 return name; 301 } 302 303 /** 304 * Maps a package name to its new name. The default implementation of this method returns the 305 * given name, unchanged. Subclasses can override. 306 * 307 * @param name the fully qualified name of the package (using dots). 308 * @return the new name of the package. 309 */ 310 public String mapPackageName(final String name) { 311 return name; 312 } 313 314 /** 315 * Maps a module name to its new name. The default implementation of this method returns the given 316 * name, unchanged. Subclasses can override. 317 * 318 * @param name the fully qualified name (using dots) of a module. 319 * @return the new name of the module. 320 */ 321 public String mapModuleName(final String name) { 322 return name; 323 } 324 325 /** 326 * Maps the internal name of a class to its new name. The default implementation of this method 327 * returns the given name, unchanged. Subclasses can override. 328 * 329 * @param internalName the internal name of a class. 330 * @return the new internal name. 331 */ 332 public String map(final String internalName) { 333 return internalName; 334 } 335}