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
59
60
61
62 private static final int MAX_NESTING_DEPTH = 512;
63
64 private static boolean identStart(final int ch) {
65 return ch == 'T' || ch == 'L';
66 }
67
68
69
70
71
72
73
74
75 public static boolean isActualParameterList(final String s) {
76 return s.startsWith("L") && s.endsWith(">;");
77 }
78
79
80
81
82
83
84
85
86 public static boolean isFormalParameterList(final String s) {
87 return s.startsWith("<") && s.indexOf(':') > 0;
88 }
89
90 private static void matchGJIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
91 matchGJIdent(in, buf, 0);
92 }
93
94 private static void matchGJIdent(final MyByteArrayInputStream in, final StringBuilder buf, final int depth) {
95 if (depth > MAX_NESTING_DEPTH) {
96 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " exceeds maximum nesting depth " + MAX_NESTING_DEPTH);
97 }
98 int ch;
99 matchIdent(in, buf);
100 ch = in.read();
101 if (ch == '<' || ch == '(') {
102
103 buf.append((char) ch);
104 matchGJIdent(in, buf, depth + 1);
105 while ((ch = in.read()) != '>' && ch != ')') {
106 if (ch == -1) {
107 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " reaching EOF");
108 }
109
110 buf.append(", ");
111 in.unread();
112 matchGJIdent(in, buf, depth + 1);
113 }
114
115 buf.append((char) ch);
116 } else {
117 in.unread();
118 }
119 ch = in.read();
120 if (identStart(ch)) {
121 in.unread();
122 matchGJIdent(in, buf, depth + 1);
123 } else if (ch == ')') {
124 in.unread();
125 } else if (ch != ';') {
126 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " read " + (char) ch);
127 }
128 }
129
130 private static void matchIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
131 int ch;
132 if ((ch = in.read()) == -1) {
133 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " no ident, reaching EOF");
134 }
135
136 if (!identStart(ch)) {
137 final StringBuilder buf2 = new StringBuilder();
138 int count = 1;
139 while (Character.isJavaIdentifierPart((char) ch)) {
140 buf2.append((char) ch);
141 count++;
142 ch = in.read();
143 }
144 if (ch == ':') {
145 final int skipExpected = "Ljava/lang/Object".length();
146 final long skipActual = in.skip(skipExpected);
147 if (skipActual != skipExpected) {
148 throw new IllegalStateException(String.format("Unexpected skip: expected=%,d, actual=%,d", skipExpected, skipActual));
149 }
150 buf.append(buf2);
151 ch = in.read();
152 in.unread();
153
154 } else {
155 for (int i = 0; i < count; i++) {
156 in.unread();
157 }
158 }
159 return;
160 }
161 final StringBuilder buf2 = new StringBuilder();
162 ch = in.read();
163 do {
164 buf2.append((char) ch);
165 ch = in.read();
166
167 } while (ch != -1 && (Character.isJavaIdentifierPart((char) ch) || ch == '/'));
168 buf.append(Utility.pathToPackage(buf2.toString()));
169
170 if (ch != -1) {
171 in.unread();
172 }
173 }
174
175
176
177
178
179
180
181 public static String translate(final String s) {
182
183 final StringBuilder buf = new StringBuilder();
184 matchGJIdent(new MyByteArrayInputStream(s), buf);
185 return buf.toString();
186 }
187
188 private int signatureIndex;
189
190
191
192
193
194
195
196
197
198
199 Signature(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
200 this(nameIndex, length, input.readUnsignedShort(), constantPool);
201 }
202
203
204
205
206
207
208
209
210
211 public Signature(final int nameIndex, final int length, final int signatureIndex, final ConstantPool constantPool) {
212 super(Const.ATTR_SIGNATURE, nameIndex, Args.require(length, 2, "Signature length attribute"), constantPool);
213 this.signatureIndex = signatureIndex;
214
215 Objects.requireNonNull(constantPool.getConstantUtf8(signatureIndex), "constantPool.getConstantUtf8(signatureIndex)");
216 }
217
218
219
220
221
222
223
224 public Signature(final Signature c) {
225 this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool());
226 }
227
228
229
230
231
232
233
234 @Override
235 public void accept(final Visitor v) {
236
237 v.visitSignature(this);
238 }
239
240
241
242
243 @Override
244 public Attribute copy(final ConstantPool constantPool) {
245 return (Attribute) clone();
246 }
247
248
249
250
251
252
253
254 @Override
255 public void dump(final DataOutputStream file) throws IOException {
256 super.dump(file);
257 file.writeShort(signatureIndex);
258 }
259
260
261
262
263
264
265 public String getSignature() {
266 return super.getConstantPool().getConstantUtf8(signatureIndex).getBytes();
267 }
268
269
270
271
272
273
274 public int getSignatureIndex() {
275 return signatureIndex;
276 }
277
278
279
280
281
282
283 public void setSignatureIndex(final int signatureIndex) {
284 this.signatureIndex = signatureIndex;
285 }
286
287
288
289
290 @Override
291 public String toString() {
292 return "Signature: " + getSignature();
293 }
294 }