1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.bcel.classfile;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.DataInput;
23 import java.io.DataOutputStream;
24 import java.io.IOException;
25 import java.nio.charset.StandardCharsets;
26 import java.util.Objects;
27
28 import org.apache.bcel.Const;
29 import org.apache.bcel.util.Args;
30
31
32
33
34
35
36 public final class Signature extends Attribute {
37
38
39
40
41 private static final class MyByteArrayInputStream extends ByteArrayInputStream {
42
43 MyByteArrayInputStream(final String data) {
44 super(data.getBytes(StandardCharsets.UTF_8));
45 }
46
47 String getData() {
48 return new String(buf, StandardCharsets.UTF_8);
49 }
50
51 void unread() {
52 if (pos > 0) {
53 pos--;
54 }
55 }
56 }
57
58 private static boolean identStart(final int ch) {
59 return ch == 'T' || ch == 'L';
60 }
61
62
63 public static boolean isActualParameterList(final String s) {
64 return s.startsWith("L") && s.endsWith(">;");
65 }
66
67
68 public static boolean isFormalParameterList(final String s) {
69 return s.startsWith("<") && s.indexOf(':') > 0;
70 }
71
72 private static void matchGJIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
73 int ch;
74 matchIdent(in, buf);
75 ch = in.read();
76 if (ch == '<' || ch == '(') {
77
78 buf.append((char) ch);
79 matchGJIdent(in, buf);
80 while ((ch = in.read()) != '>' && ch != ')') {
81 if (ch == -1) {
82 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " reaching EOF");
83 }
84
85 buf.append(", ");
86 in.unread();
87 matchGJIdent(in, buf);
88 }
89
90 buf.append((char) ch);
91 } else {
92 in.unread();
93 }
94 ch = in.read();
95 if (identStart(ch)) {
96 in.unread();
97 matchGJIdent(in, buf);
98 } else if (ch == ')') {
99 in.unread();
100 } else if (ch != ';') {
101 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " read " + (char) ch);
102 }
103 }
104
105 private static void matchIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
106 int ch;
107 if ((ch = in.read()) == -1) {
108 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " no ident, reaching EOF");
109 }
110
111 if (!identStart(ch)) {
112 final StringBuilder buf2 = new StringBuilder();
113 int count = 1;
114 while (Character.isJavaIdentifierPart((char) ch)) {
115 buf2.append((char) ch);
116 count++;
117 ch = in.read();
118 }
119 if (ch == ':') {
120 final int skipExpected = "Ljava/lang/Object".length();
121 final long skipActual = in.skip(skipExpected);
122 if (skipActual != skipExpected) {
123 throw new IllegalStateException(String.format("Unexpected skip: expected=%,d, actual=%,d", skipExpected, skipActual));
124 }
125 buf.append(buf2);
126 ch = in.read();
127 in.unread();
128
129 } else {
130 for (int i = 0; i < count; i++) {
131 in.unread();
132 }
133 }
134 return;
135 }
136 final StringBuilder buf2 = new StringBuilder();
137 ch = in.read();
138 do {
139 buf2.append((char) ch);
140 ch = in.read();
141
142 } while (ch != -1 && (Character.isJavaIdentifierPart((char) ch) || ch == '/'));
143 buf.append(Utility.pathToPackage(buf2.toString()));
144
145 if (ch != -1) {
146 in.unread();
147 }
148 }
149
150 public static String translate(final String s) {
151
152 final StringBuilder buf = new StringBuilder();
153 matchGJIdent(new MyByteArrayInputStream(s), buf);
154 return buf.toString();
155 }
156
157 private int signatureIndex;
158
159
160
161
162
163
164
165
166
167
168 Signature(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
169 this(nameIndex, length, input.readUnsignedShort(), constantPool);
170 }
171
172
173
174
175
176
177
178 public Signature(final int nameIndex, final int length, final int signatureIndex, final ConstantPool constantPool) {
179 super(Const.ATTR_SIGNATURE, nameIndex, Args.require(length, 2, "Signature length attribute"), constantPool);
180 this.signatureIndex = signatureIndex;
181
182 Objects.requireNonNull(constantPool.getConstantUtf8(signatureIndex), "constantPool.getConstantUtf8(signatureIndex)");
183 }
184
185
186
187
188
189
190
191 public Signature(final Signature c) {
192 this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool());
193 }
194
195
196
197
198
199
200
201 @Override
202 public void accept(final Visitor v) {
203
204 v.visitSignature(this);
205 }
206
207
208
209
210 @Override
211 public Attribute copy(final ConstantPool constantPool) {
212 return (Attribute) clone();
213 }
214
215
216
217
218
219
220
221 @Override
222 public void dump(final DataOutputStream file) throws IOException {
223 super.dump(file);
224 file.writeShort(signatureIndex);
225 }
226
227
228
229
230 public String getSignature() {
231 return super.getConstantPool().getConstantUtf8(signatureIndex).getBytes();
232 }
233
234
235
236
237 public int getSignatureIndex() {
238 return signatureIndex;
239 }
240
241
242
243
244 public void setSignatureIndex(final int signatureIndex) {
245 this.signatureIndex = signatureIndex;
246 }
247
248
249
250
251 @Override
252 public String toString() {
253 return "Signature: " + getSignature();
254 }
255 }