1 /*
2 * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.internal.classfile.impl;
27
28 import java.lang.classfile.*;
29 import java.lang.classfile.attribute.BootstrapMethodsAttribute;
30 import java.lang.classfile.constantpool.ClassEntry;
31 import java.lang.classfile.constantpool.ConstantPoolException;
32 import java.lang.classfile.constantpool.LoadableConstantEntry;
33 import java.lang.classfile.constantpool.PoolEntry;
34 import java.lang.classfile.constantpool.Utf8Entry;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.Objects;
39 import java.util.Optional;
40 import java.util.function.Function;
41
42 import static java.lang.classfile.constantpool.PoolEntry.*;
43
44 public final class ClassReaderImpl
45 implements ClassReader {
46 static final int CP_ITEM_START = 10;
47
48 private final byte[] buffer;
49 private final int metadataStart;
50 private final int classfileLength;
51 private final Function<Utf8Entry, AttributeMapper<?>> attributeMapper;
52 private final int flags;
53 private final int thisClassPos;
54 private ClassEntry thisClass;
55 private Optional<ClassEntry> superclass;
56 private final int constantPoolCount;
57 private final int[] cpOffset;
58
59 final ClassFileImpl context;
60 final int interfacesPos;
61 final PoolEntry[] cp;
62
63 private ClassModel containedClass;
64 private List<BootstrapMethodEntryImpl> bsmEntries;
65 private BootstrapMethodsAttribute bootstrapMethodsAttribute;
66
67 ClassReaderImpl(byte[] classfileBytes,
68 ClassFileImpl context) {
69 this.buffer = classfileBytes;
70 this.classfileLength = classfileBytes.length;
71 this.context = context;
72 this.attributeMapper = this.context.attributeMapper();
73 if (classfileLength < 4 || readInt(0) != 0xCAFEBABE) {
74 throw new IllegalArgumentException("Bad magic number");
75 }
76 if (readU2(6) > ClassFile.latestMajorVersion()) {
77 throw new IllegalArgumentException("Unsupported class file version: " + readU2(6));
78 }
79 int constantPoolCount = readU2(8);
80 int[] cpOffset = new int[constantPoolCount];
81 int p = CP_ITEM_START;
82 for (int i = 1; i < cpOffset.length; ++i) {
83 cpOffset[i] = p;
84 int tag = readU1(p);
85 ++p;
86 switch (tag) {
87 // 2
88 case TAG_CLASS, TAG_METHOD_TYPE, TAG_MODULE, TAG_STRING, TAG_PACKAGE -> p += 2;
89
90 // 3
91 case TAG_METHOD_HANDLE -> p += 3;
92
93 // 4
94 case TAG_DYNAMIC, TAG_FIELDREF, TAG_FLOAT, TAG_INTEGER,
95 TAG_INTERFACE_METHODREF, TAG_INVOKE_DYNAMIC, TAG_METHODREF,
96 TAG_NAME_AND_TYPE -> p += 4;
97
98 // 8
99 case TAG_DOUBLE, TAG_LONG -> {
100 p += 8;
101 ++i;
102 }
103 case TAG_UTF8 -> p += 2 + readU2(p);
104 default -> throw new ConstantPoolException(
105 "Bad tag (" + tag + ") at index (" + i + ") position (" + p + ")");
106 }
107 }
108 this.metadataStart = p;
109 this.cpOffset = cpOffset;
110 this.constantPoolCount = constantPoolCount;
111 this.cp = new PoolEntry[constantPoolCount];
112
113 this.flags = readU2(p);
114 this.thisClassPos = p + 2;
115 p += 6;
116 this.interfacesPos = p;
117 }
118
119 public ClassFileImpl context() {
120 return context;
121 }
122
123 @Override
124 public Function<Utf8Entry, AttributeMapper<?>> customAttributes() {
125 return attributeMapper;
126 }
127
128 @Override
129 public int size() {
130 return constantPoolCount;
131 }
132
133 @Override
134 public int flags() {
135 return flags;
136 }
137
138 @Override
139 public ClassEntry thisClassEntry() {
140 if (thisClass == null) {
141 thisClass = readEntry(thisClassPos, ClassEntry.class);
142 }
143 return thisClass;
144 }
145
146 @Override
147 public Optional<ClassEntry> superclassEntry() {
148 if (superclass == null) {
149 superclass = Optional.ofNullable(readEntryOrNull(thisClassPos + 2, ClassEntry.class));
150 }
151 return superclass;
152 }
153
154 public int thisClassPos() {
155 return thisClassPos;
156 }
157
158 @Override
159 public int classfileLength() {
160 return classfileLength;
161 }
162
163 //------ Bootstrap Method Table handling
164
165 @Override
166 public int bootstrapMethodCount() {
167 return bootstrapMethodsAttribute().bootstrapMethodsSize();
168 }
169
170 @Override
171 public BootstrapMethodEntryImpl bootstrapMethodEntry(int index) {
172 if (index < 0 || index >= bootstrapMethodCount()) {
173 throw new ConstantPoolException("Bad BSM index: " + index);
174 }
175 return bsmEntries().get(index);
176 }
177
178 private static IllegalArgumentException outOfBoundsError(IndexOutOfBoundsException cause) {
179 return new IllegalArgumentException("Reading beyond classfile bounds", cause);
180 }
181
182 @Override
183 public int readU1(int p) {
184 try {
185 return buffer[p] & 0xFF;
186 } catch (IndexOutOfBoundsException e) {
187 throw outOfBoundsError(e);
188 }
189 }
190
191 @Override
192 public int readU2(int p) {
193 try {
194 int b1 = buffer[p] & 0xFF;
195 int b2 = buffer[p + 1] & 0xFF;
196 return (b1 << 8) + b2;
197 } catch (IndexOutOfBoundsException e) {
198 throw outOfBoundsError(e);
199 }
200 }
201
202 @Override
203 public int readS1(int p) {
204 try {
205 return buffer[p];
206 } catch (IndexOutOfBoundsException e) {
207 throw outOfBoundsError(e);
208 }
209 }
210
211 @Override
212 public int readS2(int p) {
213 try {
214 int b1 = buffer[p];
215 int b2 = buffer[p + 1] & 0xFF;
216 return (b1 << 8) + b2;
217 } catch (IndexOutOfBoundsException e) {
218 throw outOfBoundsError(e);
219 }
220 }
221
222 @Override
223 public int readInt(int p) {
224 try {
225 int ch1 = buffer[p] & 0xFF;
226 int ch2 = buffer[p + 1] & 0xFF;
227 int ch3 = buffer[p + 2] & 0xFF;
228 int ch4 = buffer[p + 3] & 0xFF;
229 return (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4;
230 } catch (IndexOutOfBoundsException e) {
231 throw outOfBoundsError(e);
232 }
233 }
234
235 @Override
236 public long readLong(int p) {
237 try {
238 return ((long) buffer[p + 0] << 56) + ((long) (buffer[p + 1] & 255) << 48) +
239 ((long) (buffer[p + 2] & 255) << 40) + ((long) (buffer[p + 3] & 255) << 32) +
240 ((long) (buffer[p + 4] & 255) << 24) + ((buffer[p + 5] & 255) << 16) + ((buffer[p + 6] & 255) << 8) +
241 (buffer[p + 7] & 255);
242 } catch (IndexOutOfBoundsException e) {
243 throw outOfBoundsError(e);
244 }
245 }
246
247 @Override
248 public float readFloat(int p) {
249 return Float.intBitsToFloat(readInt(p));
250 }
251
252 @Override
253 public double readDouble(int p) {
254 return Double.longBitsToDouble(readLong(p));
255 }
256
257 @Override
258 public byte[] readBytes(int p, int len) {
259 try {
260 return Arrays.copyOfRange(buffer, p, p + len);
261 } catch (IndexOutOfBoundsException e) {
262 throw outOfBoundsError(e);
263 }
264 }
265
266 @Override
267 public void copyBytesTo(BufWriter buf, int p, int len) {
268 try {
269 buf.writeBytes(buffer, p, len);
270 } catch (IndexOutOfBoundsException e) {
271 throw outOfBoundsError(e);
272 }
273 }
274
275 BootstrapMethodsAttribute bootstrapMethodsAttribute() {
276
277 if (bootstrapMethodsAttribute == null) {
278 bootstrapMethodsAttribute
279 = containedClass.findAttribute(Attributes.bootstrapMethods())
280 .orElse(new UnboundAttribute.EmptyBootstrapAttribute());
281 }
282
283 return bootstrapMethodsAttribute;
284 }
285
286 List<BootstrapMethodEntryImpl> bsmEntries() {
287 if (bsmEntries == null) {
288 bsmEntries = new ArrayList<>();
289 BootstrapMethodsAttribute attr = bootstrapMethodsAttribute();
290 List<BootstrapMethodEntry> list = attr.bootstrapMethods();
291 if (!list.isEmpty()) {
292 for (BootstrapMethodEntry bm : list) {
293 AbstractPoolEntry.MethodHandleEntryImpl handle = (AbstractPoolEntry.MethodHandleEntryImpl) bm.bootstrapMethod();
294 List<LoadableConstantEntry> args = bm.arguments();
295 int hash = BootstrapMethodEntryImpl.computeHashCode(handle, args);
296 bsmEntries.add(new BootstrapMethodEntryImpl(this, bsmEntries.size(), hash, handle, args));
297 }
298 }
299 }
300 return bsmEntries;
301 }
302
303 void setContainedClass(ClassModel containedClass) {
304 this.containedClass = containedClass;
305 }
306
307 ClassModel getContainedClass() {
308 return containedClass;
309 }
310
311 boolean writeBootstrapMethods(BufWriterImpl buf) {
312 Optional<BootstrapMethodsAttribute> a
313 = containedClass.findAttribute(Attributes.bootstrapMethods());
314 if (a.isEmpty())
315 return false;
316 // BootstrapMethodAttribute implementations are all internal writable
317 ((Util.Writable) a.get()).writeTo(buf);
318 return true;
319 }
320
321 void writeConstantPoolEntries(BufWriter buf) {
322 copyBytesTo(buf, ClassReaderImpl.CP_ITEM_START,
323 metadataStart - ClassReaderImpl.CP_ITEM_START);
324 }
325
326 // Constantpool
327 @Override
328 public PoolEntry entryByIndex(int index) {
329 return entryByIndex(index, PoolEntry.class);
330 }
331
332 private static boolean checkTag(int tag, Class<?> cls) {
333 var type = switch (tag) {
334 // JVMS Table 4.4-B. Constant pool tags
335 case TAG_UTF8 -> AbstractPoolEntry.Utf8EntryImpl.class;
336 case TAG_INTEGER -> AbstractPoolEntry.IntegerEntryImpl.class;
337 case TAG_FLOAT -> AbstractPoolEntry.FloatEntryImpl.class;
338 case TAG_LONG -> AbstractPoolEntry.LongEntryImpl.class;
339 case TAG_DOUBLE -> AbstractPoolEntry.DoubleEntryImpl.class;
340 case TAG_CLASS -> AbstractPoolEntry.ClassEntryImpl.class;
341 case TAG_STRING -> AbstractPoolEntry.StringEntryImpl.class;
342 case TAG_FIELDREF -> AbstractPoolEntry.FieldRefEntryImpl.class;
343 case TAG_METHODREF -> AbstractPoolEntry.MethodRefEntryImpl.class;
344 case TAG_INTERFACE_METHODREF -> AbstractPoolEntry.InterfaceMethodRefEntryImpl.class;
345 case TAG_NAME_AND_TYPE -> AbstractPoolEntry.NameAndTypeEntryImpl.class;
346 case TAG_METHOD_HANDLE -> AbstractPoolEntry.MethodHandleEntryImpl.class;
347 case TAG_METHOD_TYPE -> AbstractPoolEntry.MethodTypeEntryImpl.class;
348 case TAG_DYNAMIC -> AbstractPoolEntry.ConstantDynamicEntryImpl.class;
349 case TAG_INVOKE_DYNAMIC -> AbstractPoolEntry.InvokeDynamicEntryImpl.class;
350 case TAG_MODULE -> AbstractPoolEntry.ModuleEntryImpl.class;
351 case TAG_PACKAGE -> AbstractPoolEntry.PackageEntryImpl.class;
352 default -> null;
353 };
354 return type != null && cls.isAssignableFrom(type);
355 }
356
357 static <T extends PoolEntry> T checkType(PoolEntry e, int index, Class<T> cls) {
358 if (cls.isInstance(e)) return cls.cast(e);
359 throw checkTypeError(index, cls);
360 }
361
362 private static ConstantPoolException checkTypeError(int index, Class<?> cls) {
363 return new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index);
364 }
365
366 @Override
367 public <T extends PoolEntry> T entryByIndex(int index, Class<T> cls) {
368 Objects.requireNonNull(cls);
369 if (index <= 0 || index >= constantPoolCount) {
370 throw new ConstantPoolException("Bad CP index: " + index);
371 }
372 PoolEntry info = cp[index];
373 if (info == null) {
374 int offset = cpOffset[index];
375 if (offset == 0) {
376 throw new ConstantPoolException("Unusable CP index: " + index);
377 }
378 int tag = readU1(offset);
379 if (!checkTag(tag, cls)) {
380 throw new ConstantPoolException(
381 "Bad tag (" + tag + ") at index (" + index + ") position (" + offset + "), expected " + cls.getSimpleName());
382 }
383 final int q = offset + 1;
384 info = switch (tag) {
385 case TAG_UTF8 -> new AbstractPoolEntry.Utf8EntryImpl(this, index, buffer, q + 2, readU2(q));
386 case TAG_INTEGER -> new AbstractPoolEntry.IntegerEntryImpl(this, index, readInt(q));
387 case TAG_FLOAT -> new AbstractPoolEntry.FloatEntryImpl(this, index, readFloat(q));
388 case TAG_LONG -> new AbstractPoolEntry.LongEntryImpl(this, index, readLong(q));
389 case TAG_DOUBLE -> new AbstractPoolEntry.DoubleEntryImpl(this, index, readDouble(q));
390 case TAG_CLASS -> new AbstractPoolEntry.ClassEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class));
391 case TAG_STRING -> new AbstractPoolEntry.StringEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class));
392 case TAG_FIELDREF -> new AbstractPoolEntry.FieldRefEntryImpl(this, index, readEntry(q, AbstractPoolEntry.ClassEntryImpl.class),
393 readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class));
394 case TAG_METHODREF -> new AbstractPoolEntry.MethodRefEntryImpl(this, index, readEntry(q, AbstractPoolEntry.ClassEntryImpl.class),
395 readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class));
396 case TAG_INTERFACE_METHODREF -> new AbstractPoolEntry.InterfaceMethodRefEntryImpl(this, index, readEntry(q, AbstractPoolEntry.ClassEntryImpl.class),
397 readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class));
398 case TAG_NAME_AND_TYPE -> new AbstractPoolEntry.NameAndTypeEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class),
399 readEntry(q + 2, AbstractPoolEntry.Utf8EntryImpl.class));
400 case TAG_METHOD_HANDLE -> new AbstractPoolEntry.MethodHandleEntryImpl(this, index, readU1(q),
401 readEntry(q + 1, AbstractPoolEntry.AbstractMemberRefEntry.class));
402 case TAG_METHOD_TYPE -> new AbstractPoolEntry.MethodTypeEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class));
403 case TAG_DYNAMIC -> new AbstractPoolEntry.ConstantDynamicEntryImpl(this, index, readU2(q), readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class));
404 case TAG_INVOKE_DYNAMIC -> new AbstractPoolEntry.InvokeDynamicEntryImpl(this, index, readU2(q), readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class));
405 case TAG_MODULE -> new AbstractPoolEntry.ModuleEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class));
406 case TAG_PACKAGE -> new AbstractPoolEntry.PackageEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class));
407 default -> throw new ConstantPoolException(
408 "Bad tag (" + tag + ") at index (" + index + ") position (" + offset + ")");
409 };
410 cp[index] = info;
411 }
412 return checkType(info, index, cls);
413 }
414
415 public int skipAttributeHolder(int offset) {
416 int p = offset;
417 int cnt = readU2(p);
418 p += 2;
419 for (int i = 0; i < cnt; ++i) {
420 int len = readInt(p + 2);
421 p += 6;
422 if (len < 0 || len > classfileLength - p) {
423 throw new IllegalArgumentException("attribute " + readEntry(p - 6, Utf8Entry.class).stringValue() + " too big to handle");
424 }
425 p += len;
426 }
427 return p;
428 }
429
430 @Override
431 public PoolEntry readEntry(int pos) {
432 return entryByIndex(readU2(pos));
433 }
434
435 @Override
436 public <T extends PoolEntry> T readEntry(int pos, Class<T> cls) {
437 Objects.requireNonNull(cls);
438 return entryByIndex(readU2(pos), cls);
439 }
440
441 @Override
442 public PoolEntry readEntryOrNull(int pos) {
443 int index = readU2(pos);
444 if (index == 0) {
445 return null;
446 }
447 return entryByIndex(index);
448 }
449
450 @Override
451 public <T extends PoolEntry> T readEntryOrNull(int offset, Class<T> cls) {
452 Objects.requireNonNull(cls);
453 int index = readU2(offset);
454 if (index == 0) {
455 return null;
456 }
457 return entryByIndex(index, cls);
458 }
459
460 public boolean compare(BufWriterImpl bufWriter,
461 int bufWriterOffset,
462 int classReaderOffset,
463 int length) {
464 try {
465 return Arrays.equals(bufWriter.elems,
466 bufWriterOffset, bufWriterOffset + length,
467 buffer, classReaderOffset, classReaderOffset + length);
468 } catch (IndexOutOfBoundsException e) {
469 throw outOfBoundsError(e);
470 }
471 }
472 }