1 // ASM: a very small and fast Java bytecode manipulation framework 2 // Copyright (c) 2000-2011 INRIA, France Telecom 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. Neither the name of the copyright holders nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 // THE POSSIBILITY OF SUCH DAMAGE. 28 29 package org.objectweb.asm.commons; 30 31 import org.objectweb.asm.ConstantDynamic; 32 import org.objectweb.asm.Handle; 33 import org.objectweb.asm.Opcodes; 34 import org.objectweb.asm.Type; 35 import org.objectweb.asm.signature.SignatureReader; 36 import org.objectweb.asm.signature.SignatureVisitor; 37 import org.objectweb.asm.signature.SignatureWriter; 38 39 /** 40 * A class responsible for remapping types and names. 41 * 42 * @author Eugene Kuleshov 43 */ 44 public abstract class Remapper { 45 46 /** 47 * Returns the given descriptor, remapped with {@link #map(String)}. 48 * 49 * @param descriptor a type descriptor. 50 * @return the given descriptor, with its [array element type] internal name remapped with {@link 51 * #map(String)} (if the descriptor corresponds to an array or object type, otherwise the 52 * descriptor is returned as is). See {@link Type#getInternalName()}. 53 */ mapDesc(final String descriptor)54 public String mapDesc(final String descriptor) { 55 return mapType(Type.getType(descriptor)).getDescriptor(); 56 } 57 58 /** 59 * Returns the given {@link Type}, remapped with {@link #map(String)} or {@link 60 * #mapMethodDesc(String)}. 61 * 62 * @param type a type, which can be a method type. 63 * @return the given type, with its [array element type] internal name remapped with {@link 64 * #map(String)} (if the type is an array or object type, otherwise the type is returned as 65 * is) or, of the type is a method type, with its descriptor remapped with {@link 66 * #mapMethodDesc(String)}. See {@link Type#getInternalName()}. 67 */ mapType(final Type type)68 private Type mapType(final Type type) { 69 switch (type.getSort()) { 70 case Type.ARRAY: 71 StringBuilder remappedDescriptor = new StringBuilder(); 72 for (int i = 0; i < type.getDimensions(); ++i) { 73 remappedDescriptor.append('['); 74 } 75 remappedDescriptor.append(mapType(type.getElementType()).getDescriptor()); 76 return Type.getType(remappedDescriptor.toString()); 77 case Type.OBJECT: 78 String remappedInternalName = map(type.getInternalName()); 79 return remappedInternalName != null ? Type.getObjectType(remappedInternalName) : type; 80 case Type.METHOD: 81 return Type.getMethodType(mapMethodDesc(type.getDescriptor())); 82 default: 83 return type; 84 } 85 } 86 87 /** 88 * Returns the given internal name, remapped with {@link #map(String)}. 89 * 90 * @param internalName the internal name (or array type descriptor) of some (array) class (see 91 * {@link Type#getInternalName()}). 92 * @return the given internal name, remapped with {@link #map(String)} (see {@link 93 * Type#getInternalName()}). 94 */ mapType(final String internalName)95 public String mapType(final String internalName) { 96 if (internalName == null) { 97 return null; 98 } 99 return mapType(Type.getObjectType(internalName)).getInternalName(); 100 } 101 102 /** 103 * Returns the given internal names, remapped with {@link #map(String)}. 104 * 105 * @param internalNames the internal names (or array type descriptors) of some (array) classes 106 * (see {@link Type#getInternalName()}). 107 * @return the given internal name, remapped with {@link #map(String)} (see {@link 108 * Type#getInternalName()}). 109 */ mapTypes(final String[] internalNames)110 public String[] mapTypes(final String[] internalNames) { 111 String[] remappedInternalNames = null; 112 for (int i = 0; i < internalNames.length; ++i) { 113 String internalName = internalNames[i]; 114 String remappedInternalName = mapType(internalName); 115 if (remappedInternalName != null) { 116 if (remappedInternalNames == null) { 117 remappedInternalNames = internalNames.clone(); 118 } 119 remappedInternalNames[i] = remappedInternalName; 120 } 121 } 122 return remappedInternalNames != null ? remappedInternalNames : internalNames; 123 } 124 125 /** 126 * Returns the given method descriptor, with its argument and return type descriptors remapped 127 * with {@link #mapDesc(String)}. 128 * 129 * @param methodDescriptor a method descriptor. 130 * @return the given method descriptor, with its argument and return type descriptors remapped 131 * with {@link #mapDesc(String)}. 132 */ mapMethodDesc(final String methodDescriptor)133 public String mapMethodDesc(final String methodDescriptor) { 134 if ("()V".equals(methodDescriptor)) { 135 return methodDescriptor; 136 } 137 138 StringBuilder stringBuilder = new StringBuilder("("); 139 for (Type argumentType : Type.getArgumentTypes(methodDescriptor)) { 140 stringBuilder.append(mapType(argumentType).getDescriptor()); 141 } 142 Type returnType = Type.getReturnType(methodDescriptor); 143 if (returnType == Type.VOID_TYPE) { 144 stringBuilder.append(")V"); 145 } else { 146 stringBuilder.append(')').append(mapType(returnType).getDescriptor()); 147 } 148 return stringBuilder.toString(); 149 } 150 151 /** 152 * Returns the given value, remapped with this remapper. Possible values are {@link Boolean}, 153 * {@link Byte}, {@link Short}, {@link Character}, {@link Integer}, {@link Long}, {@link Double}, 154 * {@link Float}, {@link String}, {@link Type}, {@link Handle}, {@link ConstantDynamic} or arrays 155 * of primitive types . 156 * 157 * @param value an object. Only {@link Type}, {@link Handle} and {@link ConstantDynamic} values 158 * are remapped. 159 * @return the given value, remapped with this remapper. 160 */ mapValue(final Object value)161 public Object mapValue(final Object value) { 162 if (value instanceof Type) { 163 return mapType((Type) value); 164 } 165 if (value instanceof Handle) { 166 Handle handle = (Handle) value; 167 boolean isFieldHandle = handle.getTag() <= Opcodes.H_PUTSTATIC; 168 169 return new Handle( 170 handle.getTag(), 171 mapType(handle.getOwner()), 172 isFieldHandle 173 ? mapFieldName(handle.getOwner(), handle.getName(), handle.getDesc()) 174 : mapMethodName(handle.getOwner(), handle.getName(), handle.getDesc()), 175 isFieldHandle ? mapDesc(handle.getDesc()) : mapMethodDesc(handle.getDesc()), 176 handle.isInterface()); 177 } 178 if (value instanceof ConstantDynamic) { 179 ConstantDynamic constantDynamic = (ConstantDynamic) value; 180 int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount(); 181 Object[] remappedBootstrapMethodArguments = new Object[bootstrapMethodArgumentCount]; 182 for (int i = 0; i < bootstrapMethodArgumentCount; ++i) { 183 remappedBootstrapMethodArguments[i] = 184 mapValue(constantDynamic.getBootstrapMethodArgument(i)); 185 } 186 String descriptor = constantDynamic.getDescriptor(); 187 return new ConstantDynamic( 188 mapInvokeDynamicMethodName(constantDynamic.getName(), descriptor), 189 mapDesc(descriptor), 190 (Handle) mapValue(constantDynamic.getBootstrapMethod()), 191 remappedBootstrapMethodArguments); 192 } 193 return value; 194 } 195 196 /** 197 * Returns the given signature, remapped with the {@link SignatureVisitor} returned by {@link 198 * #createSignatureRemapper(SignatureVisitor)}. 199 * 200 * @param signature a <i>JavaTypeSignature</i>, <i>ClassSignature</i> or <i>MethodSignature</i>. 201 * @param typeSignature whether the given signature is a <i>JavaTypeSignature</i>. 202 * @return signature the given signature, remapped with the {@link SignatureVisitor} returned by 203 * {@link #createSignatureRemapper(SignatureVisitor)}. 204 */ mapSignature(final String signature, final boolean typeSignature)205 public String mapSignature(final String signature, final boolean typeSignature) { 206 if (signature == null) { 207 return null; 208 } 209 SignatureReader signatureReader = new SignatureReader(signature); 210 SignatureWriter signatureWriter = new SignatureWriter(); 211 SignatureVisitor signatureRemapper = createSignatureRemapper(signatureWriter); 212 if (typeSignature) { 213 signatureReader.acceptType(signatureRemapper); 214 } else { 215 signatureReader.accept(signatureRemapper); 216 } 217 return signatureWriter.toString(); 218 } 219 220 /** 221 * Constructs a new remapper for signatures. The default implementation of this method returns a 222 * new {@link SignatureRemapper}. 223 * 224 * @param signatureVisitor the SignatureVisitor the remapper must delegate to. 225 * @return the newly created remapper. 226 * @deprecated use {@link #createSignatureRemapper} instead. 227 */ 228 @Deprecated createRemappingSignatureAdapter( final SignatureVisitor signatureVisitor)229 protected SignatureVisitor createRemappingSignatureAdapter( 230 final SignatureVisitor signatureVisitor) { 231 return createSignatureRemapper(signatureVisitor); 232 } 233 234 /** 235 * Constructs a new remapper for signatures. The default implementation of this method returns a 236 * new {@link SignatureRemapper}. 237 * 238 * @param signatureVisitor the SignatureVisitor the remapper must delegate to. 239 * @return the newly created remapper. 240 */ createSignatureRemapper(final SignatureVisitor signatureVisitor)241 protected SignatureVisitor createSignatureRemapper(final SignatureVisitor signatureVisitor) { 242 return new SignatureRemapper(signatureVisitor, this); 243 } 244 245 /** 246 * Maps an annotation attribute name. The default implementation of this method returns the given 247 * name, unchanged. Subclasses can override. 248 * 249 * @param descriptor the descriptor of the annotation class. 250 * @param name the name of the annotation attribute. 251 * @return the new name of the annotation attribute. 252 */ mapAnnotationAttributeName(final String descriptor, final String name)253 public String mapAnnotationAttributeName(final String descriptor, final String name) { 254 return name; 255 } 256 257 /** 258 * Maps an inner class name to its new name. The default implementation of this method provides a 259 * strategy that will work for inner classes produced by Java, but not necessarily other 260 * languages. Subclasses can override. 261 * 262 * @param name the fully-qualified internal name of the inner class (see {@link 263 * Type#getInternalName()}). 264 * @param ownerName the internal name of the owner class of the inner class (see {@link 265 * Type#getInternalName()}). 266 * @param innerName the internal name of the inner class (see {@link Type#getInternalName()}). 267 * @return the new inner name of the inner class. 268 */ mapInnerClassName( final String name, final String ownerName, final String innerName)269 public String mapInnerClassName( 270 final String name, final String ownerName, final String innerName) { 271 final String remappedInnerName = this.mapType(name); 272 273 if (remappedInnerName.equals(name)) { 274 return innerName; 275 } else { 276 int originSplit = name.lastIndexOf('/'); 277 int remappedSplit = remappedInnerName.lastIndexOf('/'); 278 if (originSplit != -1 && remappedSplit != -1) { 279 if (name.substring(originSplit).equals(remappedInnerName.substring(remappedSplit))) { 280 // class name not changed 281 return innerName; 282 } 283 } 284 } 285 286 if (remappedInnerName.contains("$")) { 287 int index = remappedInnerName.lastIndexOf('$') + 1; 288 while (index < remappedInnerName.length() 289 && Character.isDigit(remappedInnerName.charAt(index))) { 290 index++; 291 } 292 return remappedInnerName.substring(index); 293 } else { 294 return innerName; 295 } 296 } 297 298 /** 299 * Maps a method name to its new name. The default implementation of this method returns the given 300 * name, unchanged. Subclasses can override. 301 * 302 * @param owner the internal name of the owner class of the method (see {@link 303 * Type#getInternalName()}). 304 * @param name the name of the method. 305 * @param descriptor the descriptor of the method. 306 * @return the new name of the method. 307 */ mapMethodName(final String owner, final String name, final String descriptor)308 public String mapMethodName(final String owner, final String name, final String descriptor) { 309 return name; 310 } 311 312 /** 313 * Maps an invokedynamic or a constant dynamic method name to its new name. The default 314 * implementation of this method returns the given name, unchanged. Subclasses can override. 315 * 316 * @param name the name of the method. 317 * @param descriptor the descriptor of the method. 318 * @return the new name of the method. 319 */ mapInvokeDynamicMethodName(final String name, final String descriptor)320 public String mapInvokeDynamicMethodName(final String name, final String descriptor) { 321 return name; 322 } 323 324 /** 325 * Maps a record component name to its new name. The default implementation of this method returns 326 * the given name, unchanged. Subclasses can override. 327 * 328 * @param owner the internal name of the owner class of the field (see {@link 329 * Type#getInternalName()}). 330 * @param name the name of the field. 331 * @param descriptor the descriptor of the field. 332 * @return the new name of the field. 333 */ mapRecordComponentName( final String owner, final String name, final String descriptor)334 public String mapRecordComponentName( 335 final String owner, final String name, final String descriptor) { 336 return name; 337 } 338 339 /** 340 * Maps a field name to its new name. The default implementation of this method returns the given 341 * name, unchanged. Subclasses can override. 342 * 343 * @param owner the internal name of the owner class of the field (see {@link 344 * Type#getInternalName()}). 345 * @param name the name of the field. 346 * @param descriptor the descriptor of the field. 347 * @return the new name of the field. 348 */ mapFieldName(final String owner, final String name, final String descriptor)349 public String mapFieldName(final String owner, final String name, final String descriptor) { 350 return name; 351 } 352 353 /** 354 * Maps a package name to its new name. The default implementation of this method returns the 355 * given name, unchanged. Subclasses can override. 356 * 357 * @param name the fully qualified name of the package (using dots). 358 * @return the new name of the package. 359 */ mapPackageName(final String name)360 public String mapPackageName(final String name) { 361 return name; 362 } 363 364 /** 365 * Maps a module name to its new name. The default implementation of this method returns the given 366 * name, unchanged. Subclasses can override. 367 * 368 * @param name the fully qualified name (using dots) of a module. 369 * @return the new name of the module. 370 */ mapModuleName(final String name)371 public String mapModuleName(final String name) { 372 return name; 373 } 374 375 /** 376 * Maps the internal name of a class to its new name. The default implementation of this method 377 * returns the given name, unchanged. Subclasses can override. 378 * 379 * @param internalName the internal name of a class (see {@link Type#getInternalName()}). 380 * @return the new internal name (see {@link Type#getInternalName()}). 381 */ map(final String internalName)382 public String map(final String internalName) { 383 return internalName; 384 } 385 } 386