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}