1 /*
  2  * Copyright (c) 2022, 2023, 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.util.ArrayList;
 29 import java.util.Collections;
 30 import java.util.List;
 31 import java.util.Optional;
 32 import java.util.function.Function;
 33 
 34 import jdk.internal.classfile.*;
 35 import jdk.internal.classfile.attribute.*;
 36 import jdk.internal.classfile.constantpool.ClassEntry;
 37 import jdk.internal.classfile.constantpool.ConstantPool;
 38 import jdk.internal.classfile.constantpool.ConstantValueEntry;
 39 import jdk.internal.classfile.constantpool.LoadableConstantEntry;
 40 import jdk.internal.classfile.constantpool.ModuleEntry;
 41 import jdk.internal.classfile.constantpool.NameAndTypeEntry;
 42 import jdk.internal.classfile.constantpool.PackageEntry;
 43 import jdk.internal.classfile.constantpool.Utf8Entry;
 44 import jdk.internal.access.SharedSecrets;
 45 
 46 public abstract sealed class BoundAttribute<T extends Attribute<T>>
 47         extends AbstractElement
 48         implements Attribute<T> {
 49 
 50     static final int NAME_AND_LENGTH_PREFIX = 6;
 51     private final AttributeMapper<T> mapper;
 52     final ClassReader classReader;
 53     final int payloadStart;
 54 
 55     BoundAttribute(ClassReader classReader, AttributeMapper<T> mapper, int payloadStart) {
 56         this.mapper = mapper;
 57         this.classReader = classReader;
 58         this.payloadStart = payloadStart;
 59     }
 60 
 61     public int payloadLen() {
 62         return classReader.readInt(payloadStart - 4);
 63     }
 64 
 65     @Override
 66     public String attributeName() {
 67         return mapper.name();
 68     }
 69 
 70     @Override
 71     public AttributeMapper<T> attributeMapper() {
 72         return mapper;
 73     }
 74 
 75     public byte[] contents() {
 76         return classReader.readBytes(payloadStart, payloadLen());
 77     }
 78 
 79     @Override
 80     public void writeTo(DirectClassBuilder builder) {
 81         builder.writeAttribute(this);
 82     }
 83 
 84     @Override
 85     public void writeTo(DirectCodeBuilder builder) {
 86         builder.writeAttribute(this);
 87     }
 88 
 89     @Override
 90     public void writeTo(DirectMethodBuilder builder) {
 91         builder.writeAttribute(this);
 92     }
 93 
 94     @Override
 95     public void writeTo(DirectFieldBuilder builder) {
 96         builder.writeAttribute(this);
 97     }
 98 
 99     @Override
100     @SuppressWarnings("unchecked")
101     public void writeTo(BufWriter buf) {
102         if (!buf.canWriteDirect(classReader))
103             attributeMapper().writeAttribute(buf, (T) this);
104         else
105             classReader.copyBytesTo(buf, payloadStart - NAME_AND_LENGTH_PREFIX, payloadLen() + NAME_AND_LENGTH_PREFIX);
106     }
107 
108     public ConstantPool constantPool() {
109         return classReader;
110     }
111 
112     @Override
113     public String toString() {
114         return String.format("Attribute[name=%s]", mapper.name());
115     }
116 
117     <E> List<E> readEntryList(int p) {
118         int cnt = classReader.readU2(p);
119         p += 2;
120         var entries = new Object[cnt];
121         int end = p + (cnt * 2);
122         for (int i = 0; p < end; i++, p += 2) {
123             entries[i] = classReader.readEntry(p);
124         }
125         return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries);
126     }
127 
128     public static List<Attribute<?>> readAttributes(AttributedElement enclosing, ClassReader reader, int pos,
129                                                                   Function<Utf8Entry, AttributeMapper<?>> customAttributes) {
130         int size = reader.readU2(pos);
131         var filled = new ArrayList<Attribute<?>>(size);
132         int p = pos + 2;
133         int cfLen = reader.classfileLength();
134         var apo = ((ClassReaderImpl)reader).context().attributesProcessingOption();
135         for (int i = 0; i < size; ++i) {
136             Utf8Entry name = reader.readUtf8Entry(p);
137             int len = reader.readInt(p + 2);
138             p += 6;
139             if (len < 0 || len > cfLen - p) {
140                 throw new IllegalArgumentException("attribute " + name.stringValue() + " too big to handle");
141             }
142 
143             var mapper = Attributes.standardAttribute(name);
144             if (mapper == null) {
145                 mapper = customAttributes.apply(name);
146             }
147             if (mapper != null) {
148                 filled.add((Attribute)mapper.readAttribute(enclosing, reader, p));
149             } else {
150                 AttributeMapper<UnknownAttribute> fakeMapper = new AttributeMapper<>() {
151                     @Override
152                     public String name() {
153                         return name.stringValue();
154                     }
155 
156                     @Override
157                     public UnknownAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) {
158                         // Will never get called
159                         throw new UnsupportedOperationException();
160                     }
161 
162                     @Override
163                     public void writeAttribute(BufWriter buf, UnknownAttribute attr) {
164                         buf.writeIndex(name);
165                         var cont = attr.contents();
166                         buf.writeInt(cont.length);
167                         buf.writeBytes(cont);
168                     }
169 
170                     @Override
171                     public boolean allowMultiple() {
172                         return true;
173                     }
174 
175                     @Override
176                     public AttributeMapper.AttributeStability stability() {
177                         return AttributeStability.UNKNOWN;
178                     }
179                 };
180                 filled.add(new BoundUnknownAttribute(reader, fakeMapper, p));
181             }
182             p += len;
183         }
184         return Collections.unmodifiableList(filled);
185     }
186 
187     public static final class BoundUnknownAttribute extends BoundAttribute<UnknownAttribute>
188             implements UnknownAttribute {
189         public BoundUnknownAttribute(ClassReader cf, AttributeMapper<UnknownAttribute> mapper, int pos) {
190             super(cf, mapper, pos);
191         }
192     }
193 
194     public static final class BoundStackMapTableAttribute
195             extends BoundAttribute<StackMapTableAttribute>
196             implements StackMapTableAttribute {
197         final MethodModel method;
198         final LabelContext ctx;
199         List<StackMapFrameInfo> entries = null;
200 
201         public BoundStackMapTableAttribute(CodeImpl code, ClassReader cf, AttributeMapper<StackMapTableAttribute> mapper, int pos) {
202             super(cf, mapper, pos);
203             method = code.parent().orElseThrow();
204             ctx = code;
205         }
206 
207         @Override
208         public List<StackMapFrameInfo> entries() {
209             if (entries == null) {
210                 entries = new StackMapDecoder(classReader, payloadStart, ctx, StackMapDecoder.initFrameLocals(method)).entries();
211             }
212             return entries;
213         }
214     }
215 
216     public static final class BoundSyntheticAttribute extends BoundAttribute<SyntheticAttribute>
217             implements SyntheticAttribute {
218         public BoundSyntheticAttribute(ClassReader cf, AttributeMapper<SyntheticAttribute> mapper, int pos) {
219             super(cf, mapper, pos);
220         }
221     }
222 
223     public static final class BoundLineNumberTableAttribute
224             extends BoundAttribute<LineNumberTableAttribute>
225             implements LineNumberTableAttribute {
226         private List<LineNumberInfo> lineNumbers = null;
227 
228         public BoundLineNumberTableAttribute(ClassReader cf, AttributeMapper<LineNumberTableAttribute> mapper, int pos) {
229             super(cf, mapper, pos);
230         }
231 
232         @Override
233         public List<LineNumberInfo> lineNumbers() {
234             if (lineNumbers == null) {
235                 int nLn = classReader.readU2(payloadStart);
236                 LineNumberInfo[] elements = new LineNumberInfo[nLn];
237                 int p = payloadStart + 2;
238                 int pEnd = p + (nLn * 4);
239                 for (int i = 0; p < pEnd; p += 4, i++) {
240                     int startPc = classReader.readU2(p);
241                     int lineNumber = classReader.readU2(p + 2);
242                     elements[i] = LineNumberInfo.of(startPc, lineNumber);
243                 }
244                 lineNumbers = List.of(elements);
245             }
246             return lineNumbers;
247         }
248     }
249 
250     public static final class BoundCharacterRangeTableAttribute extends BoundAttribute<CharacterRangeTableAttribute> implements CharacterRangeTableAttribute {
251         private List<CharacterRangeInfo> characterRangeTable = null;
252 
253         public BoundCharacterRangeTableAttribute(ClassReader cf, AttributeMapper<CharacterRangeTableAttribute> mapper, int pos) {
254             super(cf, mapper, pos);
255         }
256 
257         @Override
258         public List<CharacterRangeInfo> characterRangeTable() {
259             if (characterRangeTable == null) {
260                 int nLn = classReader.readU2(payloadStart);
261                 CharacterRangeInfo[] elements = new CharacterRangeInfo[nLn];
262                 int p = payloadStart + 2;
263                 int pEnd = p + (nLn * 14);
264                 for (int i = 0; p < pEnd; p += 14, i++) {
265                     int startPc = classReader.readU2(p);
266                     int endPc = classReader.readU2(p + 2);
267                     int characterRangeStart = classReader.readInt(p + 4);
268                     int characterRangeEnd = classReader.readInt(p + 8);
269                     int flags = classReader.readU2(p + 12);
270                     elements[i] = CharacterRangeInfo.of(startPc, endPc, characterRangeStart, characterRangeEnd, flags);
271                 }
272                 characterRangeTable = List.of(elements);
273             }
274             return characterRangeTable;
275         }
276     }
277 
278     public static final class BoundLocalVariableTableAttribute
279             extends BoundAttribute<LocalVariableTableAttribute>
280             implements LocalVariableTableAttribute {
281         private final CodeImpl codeAttribute;
282         private List<LocalVariableInfo> localVars = null;
283 
284         public BoundLocalVariableTableAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<LocalVariableTableAttribute> mapper, int pos) {
285             super(cf, mapper, pos);
286             codeAttribute = (CodeImpl) enclosing;
287         }
288 
289         @Override
290         public List<LocalVariableInfo> localVariables() {
291             if (localVars == null) {
292                 int cnt = classReader.readU2(payloadStart);
293                 BoundLocalVariable[] elements = new BoundLocalVariable[cnt];
294                 int p = payloadStart + 2;
295                 int pEnd = p + (cnt * 10);
296                 for (int i = 0; p < pEnd; p += 10, i++) {
297                     elements[i] = new BoundLocalVariable(codeAttribute, p);
298                 }
299                 localVars = List.of(elements);
300             }
301             return localVars;
302         }
303     }
304 
305     public static final class BoundLocalVariableTypeTableAttribute
306             extends BoundAttribute<LocalVariableTypeTableAttribute>
307             implements LocalVariableTypeTableAttribute {
308         private final CodeImpl codeAttribute;
309         private List<LocalVariableTypeInfo> localVars = null;
310 
311         public BoundLocalVariableTypeTableAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<LocalVariableTypeTableAttribute> mapper, int pos) {
312             super(cf, mapper, pos);
313             this.codeAttribute = (CodeImpl) enclosing;
314         }
315 
316         @Override
317         public List<LocalVariableTypeInfo> localVariableTypes() {
318             if (localVars == null) {
319                 final int cnt = classReader.readU2(payloadStart);
320                 BoundLocalVariableType[] elements = new BoundLocalVariableType[cnt];
321                 int p = payloadStart + 2;
322                 int pEnd = p + (cnt * 10);
323                 for (int i = 0; p < pEnd; p += 10, i++) {
324                     elements[i] = new BoundLocalVariableType(codeAttribute, p);
325                 }
326                 localVars = List.of(elements);
327             }
328             return localVars;
329         }
330     }
331 
332     public static final class BoundMethodParametersAttribute extends BoundAttribute<MethodParametersAttribute>
333             implements MethodParametersAttribute {
334         private List<MethodParameterInfo> parameters = null;
335 
336         public BoundMethodParametersAttribute(ClassReader cf, AttributeMapper<MethodParametersAttribute> mapper, int pos) {
337             super(cf, mapper, pos);
338         }
339 
340         @Override
341         public List<MethodParameterInfo> parameters() {
342             if (parameters == null) {
343                 final int cnt = classReader.readU1(payloadStart);
344                 MethodParameterInfo[] elements = new MethodParameterInfo[cnt];
345                 int p = payloadStart + 1;
346                 int pEnd = p + (cnt * 4);
347                 for (int i = 0; p < pEnd; p += 4, i++) {
348                     Utf8Entry name = classReader.readUtf8EntryOrNull(p);
349                     int accessFlags = classReader.readU2(p + 2);
350                     elements[i] = MethodParameterInfo.of(Optional.ofNullable(name), accessFlags);
351                 }
352                 parameters = List.of(elements);
353             }
354             return parameters;
355         }
356     }
357 
358     public static final class BoundModuleHashesAttribute extends BoundAttribute<ModuleHashesAttribute>
359             implements ModuleHashesAttribute {
360         private List<ModuleHashInfo> hashes = null;
361 
362         public BoundModuleHashesAttribute(ClassReader cf, AttributeMapper<ModuleHashesAttribute> mapper, int pos) {
363             super(cf, mapper, pos);
364         }
365 
366         @Override
367         public Utf8Entry algorithm() {
368             return classReader.readUtf8Entry(payloadStart);
369         }
370 
371         @Override
372         public List<ModuleHashInfo> hashes() {
373             if (hashes == null) {
374                 final int cnt = classReader.readU2(payloadStart + 2);
375                 ModuleHashInfo[] elements = new ModuleHashInfo[cnt];
376                 int p = payloadStart + 4;
377                 //System.err.printf("%5d: ModuleHashesAttr alg = %s, cnt = %d%n", pos, algorithm(), cnt);
378                 for (int i = 0; i < cnt; ++i) {
379                     ModuleEntry module = classReader.readModuleEntry(p);
380                     int hashLength = classReader.readU2(p + 2);
381                     //System.err.printf("%5d:     [%d] module = %s, hashLength = %d%n", p, i, module, hashLength);
382                     p += 4;
383                     elements[i] = ModuleHashInfo.of(module, classReader.readBytes(p, hashLength));
384                     p += hashLength;
385                 }
386                 hashes = List.of(elements);
387             }
388             return hashes;
389         }
390     }
391 
392     public static final class BoundRecordAttribute extends BoundAttribute<RecordAttribute>
393             implements RecordAttribute {
394         private List<RecordComponentInfo> components = null;
395 
396         public BoundRecordAttribute(ClassReader cf, AttributeMapper<RecordAttribute> mapper, int pos) {
397             super(cf, mapper, pos);
398         }
399 
400         @Override
401         public List<RecordComponentInfo> components() {
402             if (components == null) {
403                 final int cnt = classReader.readU2(payloadStart);
404                 RecordComponentInfo[] elements = new RecordComponentInfo[cnt];
405                 int p = payloadStart + 2;
406                 for (int i = 0; i < cnt; i++) {
407                     elements[i] = new BoundRecordComponentInfo(classReader, p);
408                     p = classReader.skipAttributeHolder(p + 4);
409                 }
410                 components = List.of(elements);
411             }
412             return components;
413         }
414     }
415 
416     public static final class BoundDeprecatedAttribute extends BoundAttribute<DeprecatedAttribute>
417             implements DeprecatedAttribute {
418         public BoundDeprecatedAttribute(ClassReader cf, AttributeMapper<DeprecatedAttribute> mapper, int pos) {
419             super(cf, mapper, pos);
420         }
421     }
422 
423     public static final class BoundSignatureAttribute extends BoundAttribute<SignatureAttribute>
424             implements SignatureAttribute {
425         public BoundSignatureAttribute(ClassReader cf, AttributeMapper<SignatureAttribute> mapper, int pos) {
426             super(cf, mapper, pos);
427         }
428 
429         @Override
430         public Utf8Entry signature() {
431             return classReader.readUtf8Entry(payloadStart);
432         }
433     }
434 
435     public static final class BoundSourceFileAttribute extends BoundAttribute<SourceFileAttribute>
436             implements SourceFileAttribute {
437         public BoundSourceFileAttribute(ClassReader cf, AttributeMapper<SourceFileAttribute> mapper, int pos) {
438             super(cf, mapper, pos);
439         }
440 
441         @Override
442         public Utf8Entry sourceFile() {
443             return classReader.readUtf8Entry(payloadStart);
444         }
445 
446     }
447 
448     public static final class BoundModuleMainClassAttribute extends BoundAttribute<ModuleMainClassAttribute> implements ModuleMainClassAttribute {
449         public BoundModuleMainClassAttribute(ClassReader cf, AttributeMapper<ModuleMainClassAttribute> mapper, int pos) {
450             super(cf, mapper, pos);
451         }
452 
453         @Override
454         public ClassEntry mainClass() {
455             return classReader.readClassEntry(payloadStart);
456         }
457     }
458 
459     public static final class BoundNestHostAttribute extends BoundAttribute<NestHostAttribute>
460             implements NestHostAttribute {
461         public BoundNestHostAttribute(ClassReader cf, AttributeMapper<NestHostAttribute> mapper, int pos) {
462             super(cf, mapper, pos);
463         }
464 
465         @Override
466         public ClassEntry nestHost() {
467             return classReader.readClassEntry(payloadStart);
468         }
469     }
470 
471     public static final class BoundSourceDebugExtensionAttribute extends BoundAttribute<SourceDebugExtensionAttribute>
472             implements SourceDebugExtensionAttribute {
473         public BoundSourceDebugExtensionAttribute(ClassReader cf, AttributeMapper<SourceDebugExtensionAttribute> mapper, int pos) {
474             super(cf, mapper, pos);
475         }
476     }
477 
478     public static final class BoundConstantValueAttribute extends BoundAttribute<ConstantValueAttribute>
479             implements ConstantValueAttribute {
480         public BoundConstantValueAttribute(ClassReader cf, AttributeMapper<ConstantValueAttribute> mapper, int pos) {
481             super(cf, mapper, pos);
482         }
483 
484         @Override
485         public ConstantValueEntry constant() {
486             return classReader.readEntry(payloadStart, ConstantValueEntry.class);
487         }
488 
489     }
490 
491     public static final class BoundModuleTargetAttribute extends BoundAttribute<ModuleTargetAttribute>
492             implements ModuleTargetAttribute {
493         public BoundModuleTargetAttribute(ClassReader cf, AttributeMapper<ModuleTargetAttribute> mapper, int pos) {
494             super(cf, mapper, pos);
495         }
496 
497         @Override
498         public Utf8Entry targetPlatform() {
499             return classReader.readUtf8Entry(payloadStart);
500         }
501     }
502 
503     public static final class BoundCompilationIDAttribute extends BoundAttribute<CompilationIDAttribute>
504             implements CompilationIDAttribute {
505         public BoundCompilationIDAttribute(ClassReader cf, AttributeMapper<CompilationIDAttribute> mapper, int pos) {
506             super(cf, mapper, pos);
507         }
508 
509         @Override
510         public Utf8Entry compilationId() {
511             return classReader.readUtf8Entry(payloadStart);
512         }
513     }
514 
515     public static final class BoundSourceIDAttribute extends BoundAttribute<SourceIDAttribute>
516             implements SourceIDAttribute {
517         public BoundSourceIDAttribute(ClassReader cf, AttributeMapper<SourceIDAttribute> mapper, int pos) {
518             super(cf, mapper, pos);
519         }
520 
521         @Override
522         public Utf8Entry sourceId() {
523             return classReader.readUtf8Entry(payloadStart);
524         }
525     }
526 
527     public static final class BoundModuleResolutionAttribute extends BoundAttribute<ModuleResolutionAttribute>
528             implements ModuleResolutionAttribute {
529         public BoundModuleResolutionAttribute(ClassReader cf, AttributeMapper<ModuleResolutionAttribute> mapper, int pos) {
530             super(cf, mapper, pos);
531         }
532 
533         @Override
534         public int resolutionFlags() {
535             return classReader.readU2(payloadStart);
536         }
537     }
538 
539     public static final class BoundExceptionsAttribute extends BoundAttribute<ExceptionsAttribute>
540             implements ExceptionsAttribute {
541         private List<ClassEntry> exceptions = null;
542 
543         public BoundExceptionsAttribute(ClassReader cf, AttributeMapper<ExceptionsAttribute> mapper, int pos) {
544             super(cf, mapper, pos);
545         }
546 
547         @Override
548         public List<ClassEntry> exceptions() {
549             if (exceptions == null) {
550                 exceptions = readEntryList(payloadStart);
551             }
552             return exceptions;
553         }
554     }
555 
556     public static final class BoundModuleAttribute extends BoundAttribute<ModuleAttribute>
557             implements ModuleAttribute {
558         private List<ModuleRequireInfo> requires = null;
559         private List<ModuleExportInfo> exports = null;
560         private List<ModuleOpenInfo> opens = null;
561         private List<ClassEntry> uses = null;
562         private List<ModuleProvideInfo> provides = null;
563 
564         public BoundModuleAttribute(ClassReader cf, AttributeMapper<ModuleAttribute> mapper, int pos) {
565             super(cf, mapper, pos);
566         }
567 
568         @Override
569         public ModuleEntry moduleName() {
570             return classReader.readModuleEntry(payloadStart);
571         }
572 
573         @Override
574         public int moduleFlagsMask() {
575             return classReader.readU2(payloadStart + 2);
576         }
577 
578         @Override
579         public Optional<Utf8Entry> moduleVersion() {
580             return Optional.ofNullable(classReader.readUtf8EntryOrNull(payloadStart + 4));
581         }
582 
583         @Override
584         public List<ModuleRequireInfo> requires() {
585             if (requires == null) {
586                 structure();
587             }
588             return requires;
589         }
590 
591         @Override
592         public List<ModuleExportInfo> exports() {
593             if (exports == null) {
594                 structure();
595             }
596             return exports;
597         }
598 
599         @Override
600         public List<ModuleOpenInfo> opens() {
601             if (opens == null) {
602                 structure();
603             }
604             return opens;
605         }
606 
607         @Override
608         public List<ClassEntry> uses() {
609             if (uses == null) {
610                 structure();
611             }
612             return uses;
613         }
614 
615         @Override
616         public List<ModuleProvideInfo> provides() {
617             if (provides == null) {
618                 structure();
619             }
620             return provides;
621         }
622 
623         private void structure() {
624             int p = payloadStart + 8;
625 
626             {
627                 int cnt = classReader.readU2(payloadStart + 6);
628                 ModuleRequireInfo[] elements = new ModuleRequireInfo[cnt];
629                 int end = p + (cnt * 6);
630                 for (int i = 0; p < end; p += 6, i++) {
631                     elements[i] = ModuleRequireInfo.of(classReader.readModuleEntry(p),
632                             classReader.readU2(p + 2),
633                             (Utf8Entry) classReader.readEntryOrNull(p + 4));
634                 }
635                 requires = List.of(elements);
636             }
637 
638             {
639                 int cnt = classReader.readU2(p);
640                 p += 2;
641                 ModuleExportInfo[] elements = new ModuleExportInfo[cnt];
642                 for (int i = 0; i < cnt; i++) {
643                     PackageEntry pe = classReader.readPackageEntry(p);
644                     int exportFlags = classReader.readU2(p + 2);
645                     p += 4;
646                     List<ModuleEntry> exportsTo = readEntryList(p);
647                     p += 2 + exportsTo.size() * 2;
648                     elements[i] = ModuleExportInfo.of(pe, exportFlags, exportsTo);
649                 }
650                 exports = List.of(elements);
651             }
652 
653             {
654                 int cnt = classReader.readU2(p);
655                 p += 2;
656                 ModuleOpenInfo[] elements = new ModuleOpenInfo[cnt];
657                 for (int i = 0; i < cnt; i++) {
658                     PackageEntry po = classReader.readPackageEntry(p);
659                     int opensFlags = classReader.readU2(p + 2);
660                     p += 4;
661                     List<ModuleEntry> opensTo = readEntryList(p);
662                     p += 2 + opensTo.size() * 2;
663                     elements[i] = ModuleOpenInfo.of(po, opensFlags, opensTo);
664                 }
665                 opens = List.of(elements);
666             }
667 
668             {
669                 uses = readEntryList(p);
670                 p += 2 + uses.size() * 2;
671                 int cnt = classReader.readU2(p);
672                 p += 2;
673                 ModuleProvideInfo[] elements = new ModuleProvideInfo[cnt];
674                 provides = new ArrayList<>(cnt);
675                 for (int i = 0; i < cnt; i++) {
676                     ClassEntry c = classReader.readClassEntry(p);
677                     p += 2;
678                     List<ClassEntry> providesWith = readEntryList(p);
679                     p += 2 + providesWith.size() * 2;
680                     elements[i] = ModuleProvideInfo.of(c, providesWith);
681                 }
682                 provides = List.of(elements);
683             }
684         }
685     }
686 
687     public static final class BoundModulePackagesAttribute extends BoundAttribute<ModulePackagesAttribute>
688             implements ModulePackagesAttribute {
689         private List<PackageEntry> packages = null;
690 
691         public BoundModulePackagesAttribute(ClassReader cf, AttributeMapper<ModulePackagesAttribute> mapper, int pos) {
692             super(cf, mapper, pos);
693         }
694 
695         @Override
696         public List<PackageEntry> packages() {
697             if (packages == null) {
698                 packages = readEntryList(payloadStart);
699             }
700             return packages;
701         }
702     }
703 
704     public static final class BoundNestMembersAttribute extends BoundAttribute<NestMembersAttribute>
705             implements NestMembersAttribute {
706 
707         private List<ClassEntry> members = null;
708 
709         public BoundNestMembersAttribute(ClassReader cf, AttributeMapper<NestMembersAttribute> mapper, int pos) {
710             super(cf, mapper, pos);
711         }
712 
713         @Override
714         public List<ClassEntry> nestMembers() {
715             if (members == null) {
716                 members = readEntryList(payloadStart);
717             }
718             return members;
719         }
720     }
721 
722     public static final class BoundBootstrapMethodsAttribute extends BoundAttribute<BootstrapMethodsAttribute>
723             implements BootstrapMethodsAttribute {
724 
725         private List<BootstrapMethodEntry> bootstraps = null;
726         private final int size;
727 
728         public BoundBootstrapMethodsAttribute(ClassReader reader, AttributeMapper<BootstrapMethodsAttribute> mapper, int pos) {
729             super(reader, mapper, pos);
730             size = classReader.readU2(pos);
731         }
732 
733         @Override
734         public int bootstrapMethodsSize() {
735             return size;
736         }
737 
738         @Override
739         public List<BootstrapMethodEntry> bootstrapMethods() {
740             if (bootstraps == null) {
741                 BootstrapMethodEntry[] bs = new BootstrapMethodEntry[size];
742                 int p = payloadStart + 2;
743                 for (int i = 0; i < size; ++i) {
744                     final AbstractPoolEntry.MethodHandleEntryImpl handle
745                             = (AbstractPoolEntry.MethodHandleEntryImpl) classReader.readMethodHandleEntry(p);
746                     final List<LoadableConstantEntry> args = readEntryList(p + 2);
747                     p += 4 + args.size() * 2;
748                     int hash = BootstrapMethodEntryImpl.computeHashCode(handle, args);
749                     bs[i] = new BootstrapMethodEntryImpl(classReader, i, hash, handle, args);
750                 }
751                 bootstraps = List.of(bs);
752             }
753             return bootstraps;
754         }
755     }
756 
757     public static final class BoundInnerClassesAttribute extends BoundAttribute<InnerClassesAttribute>
758             implements InnerClassesAttribute {
759         private List<InnerClassInfo> classes;
760 
761         public BoundInnerClassesAttribute(ClassReader cf, AttributeMapper<InnerClassesAttribute> mapper, int pos) {
762             super(cf, mapper, pos);
763         }
764 
765         @Override
766         public List<InnerClassInfo> classes() {
767             if (classes == null) {
768                 final int cnt = classReader.readU2(payloadStart);
769                 int p = payloadStart + 2;
770                 InnerClassInfo[] elements = new InnerClassInfo[cnt];
771                 for (int i = 0; i < cnt; i++) {
772                     ClassEntry innerClass = classReader.readClassEntry(p); // TODO FIXME
773                     int outerClassIndex = classReader.readU2(p + 2);
774                     ClassEntry outerClass = outerClassIndex == 0
775                             ? null
776                             : (ClassEntry) classReader.entryByIndex(outerClassIndex);
777                     int innerNameIndex = classReader.readU2(p + 4);
778                     Utf8Entry innerName = innerNameIndex == 0
779                             ? null
780                             : (Utf8Entry) classReader.entryByIndex(innerNameIndex);
781                     int flags = classReader.readU2(p + 6);
782                     p += 8;
783                     elements[i] = InnerClassInfo.of(innerClass, Optional.ofNullable(outerClass), Optional.ofNullable(innerName), flags);
784                 }
785                 classes = List.of(elements);
786             }
787             return classes;
788         }
789     }
790 
791     public static final class BoundEnclosingMethodAttribute extends BoundAttribute<EnclosingMethodAttribute>
792             implements EnclosingMethodAttribute {
793         public BoundEnclosingMethodAttribute(ClassReader cf, AttributeMapper<EnclosingMethodAttribute> mapper, int pos) {
794             super(cf, mapper, pos);
795         }
796 
797         @Override
798         public ClassEntry enclosingClass() {
799             return classReader.readClassEntry(payloadStart);
800         }
801 
802         @Override
803         public Optional<NameAndTypeEntry> enclosingMethod() {
804             return Optional.ofNullable((NameAndTypeEntry) classReader.readEntryOrNull(payloadStart + 2));
805         }
806     }
807 
808     public static final class BoundAnnotationDefaultAttr
809             extends BoundAttribute<AnnotationDefaultAttribute>
810             implements AnnotationDefaultAttribute {
811         private AnnotationValue annotationValue;
812 
813         public BoundAnnotationDefaultAttr(ClassReader cf, AttributeMapper<AnnotationDefaultAttribute> mapper, int pos) {
814             super(cf, mapper, pos);
815         }
816 
817         @Override
818         public AnnotationValue defaultValue() {
819             if (annotationValue == null)
820                 annotationValue = AnnotationReader.readElementValue(classReader, payloadStart);
821             return annotationValue;
822         }
823     }
824 
825     public static final class BoundRuntimeVisibleTypeAnnotationsAttribute extends BoundAttribute<RuntimeVisibleTypeAnnotationsAttribute>
826             implements RuntimeVisibleTypeAnnotationsAttribute {
827 
828         private final LabelContext labelContext;
829 
830         public BoundRuntimeVisibleTypeAnnotationsAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<RuntimeVisibleTypeAnnotationsAttribute> mapper, int pos) {
831             super(cf, mapper, pos);
832             this.labelContext = (enclosing instanceof LabelContext lc) ? lc : null;
833         }
834 
835         @Override
836         public List<TypeAnnotation> annotations() {
837             return AnnotationReader.readTypeAnnotations(classReader, payloadStart, labelContext);
838         }
839     }
840 
841     public static final class BoundRuntimeInvisibleTypeAnnotationsAttribute
842             extends BoundAttribute<RuntimeInvisibleTypeAnnotationsAttribute>
843             implements RuntimeInvisibleTypeAnnotationsAttribute {
844         public BoundRuntimeInvisibleTypeAnnotationsAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<RuntimeInvisibleTypeAnnotationsAttribute> mapper, int pos) {
845             super(cf, mapper, pos);
846             this.labelContext = (enclosing instanceof LabelContext lc) ? lc : null;
847         }
848 
849         private final LabelContext labelContext;
850 
851         @Override
852         public List<TypeAnnotation> annotations() {
853             return AnnotationReader.readTypeAnnotations(classReader, payloadStart, labelContext);
854         }
855     }
856 
857     public static final class BoundRuntimeVisibleParameterAnnotationsAttribute
858             extends BoundAttribute<RuntimeVisibleParameterAnnotationsAttribute>
859             implements RuntimeVisibleParameterAnnotationsAttribute {
860 
861         public BoundRuntimeVisibleParameterAnnotationsAttribute(ClassReader cf, AttributeMapper<RuntimeVisibleParameterAnnotationsAttribute> mapper, int pos) {
862             super(cf, mapper, pos);
863         }
864 
865         @Override
866         public List<List<Annotation>> parameterAnnotations() {
867             return AnnotationReader.readParameterAnnotations(classReader, payloadStart);
868         }
869     }
870 
871     public static final class BoundRuntimeInvisibleParameterAnnotationsAttribute
872             extends BoundAttribute<RuntimeInvisibleParameterAnnotationsAttribute>
873             implements RuntimeInvisibleParameterAnnotationsAttribute {
874 
875         public BoundRuntimeInvisibleParameterAnnotationsAttribute(ClassReader cf, AttributeMapper<RuntimeInvisibleParameterAnnotationsAttribute> mapper, int pos) {
876             super(cf, mapper, pos);
877         }
878 
879         @Override
880         public List<List<Annotation>> parameterAnnotations() {
881             return AnnotationReader.readParameterAnnotations(classReader, payloadStart);
882         }
883     }
884 
885     public static final class BoundRuntimeInvisibleAnnotationsAttribute
886             extends BoundAttribute<RuntimeInvisibleAnnotationsAttribute>
887             implements RuntimeInvisibleAnnotationsAttribute {
888         private List<Annotation> inflated;
889 
890         public BoundRuntimeInvisibleAnnotationsAttribute(ClassReader cf,
891                                                          int payloadStart) {
892             super(cf, Attributes.RUNTIME_INVISIBLE_ANNOTATIONS, payloadStart);
893         }
894 
895         @Override
896         public List<Annotation> annotations() {
897             if (inflated == null)
898                 inflated = AnnotationReader.readAnnotations(classReader, payloadStart);
899             return inflated;
900         }
901     }
902 
903     public static final class BoundRuntimeVisibleAnnotationsAttribute
904             extends BoundAttribute<RuntimeVisibleAnnotationsAttribute>
905             implements RuntimeVisibleAnnotationsAttribute {
906         private List<Annotation> inflated;
907 
908         public BoundRuntimeVisibleAnnotationsAttribute(ClassReader cf,
909                                                        int payloadStart) {
910             super(cf, Attributes.RUNTIME_VISIBLE_ANNOTATIONS, payloadStart);
911         }
912 
913         @Override
914         public List<Annotation> annotations() {
915             if (inflated == null)
916                 inflated = AnnotationReader.readAnnotations(classReader, payloadStart);
917             return inflated;
918         }
919     }
920 
921     public static final class BoundPermittedSubclassesAttribute extends BoundAttribute<PermittedSubclassesAttribute>
922             implements PermittedSubclassesAttribute {
923         private List<ClassEntry> permittedSubclasses = null;
924 
925         public BoundPermittedSubclassesAttribute(ClassReader cf, AttributeMapper<PermittedSubclassesAttribute> mapper, int pos) {
926             super(cf, mapper, pos);
927         }
928 
929         @Override
930         public List<ClassEntry> permittedSubclasses() {
931             if (permittedSubclasses == null) {
932                 permittedSubclasses = readEntryList(payloadStart);
933             }
934             return permittedSubclasses;
935         }
936     }
937 
938     public static abstract sealed class BoundCodeAttribute
939             extends BoundAttribute<CodeAttribute>
940             implements CodeAttribute
941             permits CodeImpl {
942         protected final int codeStart;
943         protected final int codeLength;
944         protected final int codeEnd;
945         protected final int attributePos;
946         protected final int exceptionHandlerPos;
947         protected final int exceptionHandlerCnt;
948         protected final MethodModel enclosingMethod;
949 
950         public BoundCodeAttribute(AttributedElement enclosing,
951                                   ClassReader reader,
952                                   AttributeMapper<CodeAttribute> mapper,
953                                   int payloadStart) {
954             super(reader, mapper, payloadStart);
955             this.codeLength = classReader.readInt(payloadStart + 4);
956             this.enclosingMethod = (MethodModel) enclosing;
957             this.codeStart = payloadStart + 8;
958             this.codeEnd = codeStart + codeLength;
959             this.exceptionHandlerPos = codeEnd;
960             this.exceptionHandlerCnt = classReader.readU2(exceptionHandlerPos);
961             this.attributePos = exceptionHandlerPos + 2 + exceptionHandlerCnt * 8;
962         }
963 
964         // CodeAttribute
965 
966         @Override
967         public int maxStack() {
968             return classReader.readU2(payloadStart);
969         }
970 
971         @Override
972         public int maxLocals() {
973             return classReader.readU2(payloadStart + 2);
974         }
975 
976         @Override
977         public int codeLength() {
978             return codeLength;
979         }
980 
981         @Override
982         public byte[] codeArray() {
983             return classReader.readBytes(payloadStart + 8, codeLength());
984         }
985     }
986 }