Signature.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.classfile;

  18. import java.io.ByteArrayInputStream;
  19. import java.io.DataInput;
  20. import java.io.DataOutputStream;
  21. import java.io.IOException;
  22. import java.nio.charset.StandardCharsets;
  23. import java.util.Objects;

  24. import org.apache.bcel.Const;
  25. import org.apache.bcel.util.Args;

  26. /**
  27.  * This class is derived from <em>Attribute</em> and represents a reference to a GJ attribute.
  28.  *
  29.  * @see Attribute
  30.  */
  31. public final class Signature extends Attribute {

  32.     /**
  33.      * Extends ByteArrayInputStream to make 'unreading' chars possible.
  34.      */
  35.     private static final class MyByteArrayInputStream extends ByteArrayInputStream {

  36.         MyByteArrayInputStream(final String data) {
  37.             super(data.getBytes(StandardCharsets.UTF_8));
  38.         }

  39.         String getData() {
  40.             return new String(buf, StandardCharsets.UTF_8);
  41.         }

  42.         void unread() {
  43.             if (pos > 0) {
  44.                 pos--;
  45.             }
  46.         }
  47.     }

  48.     private static boolean identStart(final int ch) {
  49.         return ch == 'T' || ch == 'L';
  50.     }

  51.     // @since 6.0 is no longer final
  52.     public static boolean isActualParameterList(final String s) {
  53.         return s.startsWith("L") && s.endsWith(">;");
  54.     }

  55.     // @since 6.0 is no longer final
  56.     public static boolean isFormalParameterList(final String s) {
  57.         return s.startsWith("<") && s.indexOf(':') > 0;
  58.     }

  59.     private static void matchGJIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
  60.         int ch;
  61.         matchIdent(in, buf);
  62.         ch = in.read();
  63.         if (ch == '<' || ch == '(') { // Parameterized or method
  64.             // System.out.println("Enter <");
  65.             buf.append((char) ch);
  66.             matchGJIdent(in, buf);
  67.             while ((ch = in.read()) != '>' && ch != ')') { // List of parameters
  68.                 if (ch == -1) {
  69.                     throw new IllegalArgumentException("Illegal signature: " + in.getData() + " reaching EOF");
  70.                 }
  71.                 // System.out.println("Still no >");
  72.                 buf.append(", ");
  73.                 in.unread();
  74.                 matchGJIdent(in, buf); // Recursive call
  75.             }
  76.             // System.out.println("Exit >");
  77.             buf.append((char) ch);
  78.         } else {
  79.             in.unread();
  80.         }
  81.         ch = in.read();
  82.         if (identStart(ch)) {
  83.             in.unread();
  84.             matchGJIdent(in, buf);
  85.         } else if (ch == ')') {
  86.             in.unread();
  87.         } else if (ch != ';') {
  88.             throw new IllegalArgumentException("Illegal signature: " + in.getData() + " read " + (char) ch);
  89.         }
  90.     }

  91.     private static void matchIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
  92.         int ch;
  93.         if ((ch = in.read()) == -1) {
  94.             throw new IllegalArgumentException("Illegal signature: " + in.getData() + " no ident, reaching EOF");
  95.         }
  96.         // System.out.println("return from ident:" + (char) ch);
  97.         if (!identStart(ch)) {
  98.             final StringBuilder buf2 = new StringBuilder();
  99.             int count = 1;
  100.             while (Character.isJavaIdentifierPart((char) ch)) {
  101.                 buf2.append((char) ch);
  102.                 count++;
  103.                 ch = in.read();
  104.             }
  105.             if (ch == ':') { // Ok, formal parameter
  106.                 final int skipExpected = "Ljava/lang/Object".length();
  107.                 final long skipActual = in.skip(skipExpected);
  108.                 if (skipActual != skipExpected) {
  109.                     throw new IllegalStateException(String.format("Unexpected skip: expected=%,d, actual=%,d", skipExpected, skipActual));
  110.                 }
  111.                 buf.append(buf2);
  112.                 ch = in.read();
  113.                 in.unread();
  114.                 // System.out.println("so far:" + buf2 + ":next:" +(char) ch);
  115.             } else {
  116.                 for (int i = 0; i < count; i++) {
  117.                     in.unread();
  118.                 }
  119.             }
  120.             return;
  121.         }
  122.         final StringBuilder buf2 = new StringBuilder();
  123.         ch = in.read();
  124.         do {
  125.             buf2.append((char) ch);
  126.             ch = in.read();
  127.             // System.out.println("within ident:"+ (char) ch);
  128.         } while (ch != -1 && (Character.isJavaIdentifierPart((char) ch) || ch == '/'));
  129.         buf.append(Utility.pathToPackage(buf2.toString()));
  130.         // System.out.println("regular return ident:"+ (char) ch + ":" + buf2);
  131.         if (ch != -1) {
  132.             in.unread();
  133.         }
  134.     }

  135.     public static String translate(final String s) {
  136.         // System.out.println("Sig:" + s);
  137.         final StringBuilder buf = new StringBuilder();
  138.         matchGJIdent(new MyByteArrayInputStream(s), buf);
  139.         return buf.toString();
  140.     }

  141.     private int signatureIndex;

  142.     /**
  143.      * Constructs object from file stream.
  144.      *
  145.      * @param nameIndex Index in constant pool to CONSTANT_Utf8
  146.      * @param length Content length in bytes
  147.      * @param input Input stream
  148.      * @param constantPool Array of constants
  149.      * @throws IOException if an I/O error occurs.
  150.      */
  151.     Signature(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
  152.         this(nameIndex, length, input.readUnsignedShort(), constantPool);
  153.     }

  154.     /**
  155.      * @param nameIndex Index in constant pool to CONSTANT_Utf8
  156.      * @param length Content length in bytes
  157.      * @param signatureIndex Index in constant pool to CONSTANT_Utf8
  158.      * @param constantPool Array of constants
  159.      */
  160.     public Signature(final int nameIndex, final int length, final int signatureIndex, final ConstantPool constantPool) {
  161.         super(Const.ATTR_SIGNATURE, nameIndex, Args.require(length, 2, "Signature length attribute"), constantPool);
  162.         this.signatureIndex = signatureIndex;
  163.         // validate:
  164.         Objects.requireNonNull(constantPool.getConstantUtf8(signatureIndex), "constantPool.getConstantUtf8(signatureIndex)");
  165.     }

  166.     /**
  167.      * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
  168.      * physical copy.
  169.      *
  170.      * @param c Source to copy.
  171.      */
  172.     public Signature(final Signature c) {
  173.         this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool());
  174.     }

  175.     /**
  176.      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
  177.      * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
  178.      *
  179.      * @param v Visitor object
  180.      */
  181.     @Override
  182.     public void accept(final Visitor v) {
  183.         // System.err.println("Visiting non-standard Signature object");
  184.         v.visitSignature(this);
  185.     }

  186.     /**
  187.      * @return deep copy of this attribute
  188.      */
  189.     @Override
  190.     public Attribute copy(final ConstantPool constantPool) {
  191.         return (Attribute) clone();
  192.     }

  193.     /**
  194.      * Dump source file attribute to file stream in binary format.
  195.      *
  196.      * @param file Output file stream
  197.      * @throws IOException if an I/O error occurs.
  198.      */
  199.     @Override
  200.     public void dump(final DataOutputStream file) throws IOException {
  201.         super.dump(file);
  202.         file.writeShort(signatureIndex);
  203.     }

  204.     /**
  205.      * @return GJ signature.
  206.      */
  207.     public String getSignature() {
  208.         return super.getConstantPool().getConstantUtf8(signatureIndex).getBytes();
  209.     }

  210.     /**
  211.      * @return Index in constant pool of source file name.
  212.      */
  213.     public int getSignatureIndex() {
  214.         return signatureIndex;
  215.     }

  216.     /**
  217.      * @param signatureIndex the index info the constant pool of this signature
  218.      */
  219.     public void setSignatureIndex(final int signatureIndex) {
  220.         this.signatureIndex = signatureIndex;
  221.     }

  222.     /**
  223.      * @return String representation
  224.      */
  225.     @Override
  226.     public String toString() {
  227.         return "Signature: " + getSignature();
  228.     }
  229. }