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.*;
  30 import java.lang.classfile.constantpool.*;
  31 import java.util.ArrayList;
  32 import java.util.Collections;
  33 import java.util.List;
  34 import java.util.Objects;
  35 import java.util.Optional;
  36 import java.util.function.Function;
  37 
  38 import jdk.internal.access.SharedSecrets;
  39 
  40 import static java.lang.classfile.Attributes.*;
  41 
  42 public abstract sealed class BoundAttribute<T extends Attribute<T>>
  43         extends AbstractElement
  44         implements Attribute<T>, Util.Writable {
  45 
  46     static final int NAME_AND_LENGTH_PREFIX = 6;
  47     private final AttributeMapper<T> mapper;
  48     final ClassReaderImpl classReader;
  49     final int payloadStart;
  50     Utf8Entry name;
  51 
  52     BoundAttribute(ClassReader classReader, AttributeMapper<T> mapper, int payloadStart) {
  53         this.mapper = mapper;
  54         this.classReader = (ClassReaderImpl)classReader;
  55         this.payloadStart = payloadStart;
  56     }
  57 
  58     public int payloadLen() {
  59         return classReader.readInt(payloadStart - 4);
  60     }
  61 
  62     @Override
  63     public Utf8Entry attributeName() {
  64         if (name == null) {
  65             name = classReader.readEntry(payloadStart - 6, Utf8Entry.class);
  66         }
  67         return 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(BufWriterImpl 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 extends PoolEntry> List<E> readEntryList(int p, Class<E> type) {
 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, type);
 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         for (int i = 0; i < size; ++i) {
 135             Utf8Entry name = reader.readEntry(p, Utf8Entry.class);
 136             int len = reader.readInt(p + 2);
 137             p += 6;
 138             if (len < 0 || len > cfLen - p) {
 139                 throw new IllegalArgumentException("attribute " + name.stringValue() + " too big to handle");
 140             }
 141 
 142             var mapper = standardAttribute(name);
 143             if (mapper == null) {
 144                 mapper = customAttributes.apply(name);
 145             }
 146             if (mapper != null) {
 147                 filled.add(Objects.requireNonNull(mapper.readAttribute(enclosing, reader, p)));
 148             } else {
 149                 AttributeMapper<UnknownAttribute> fakeMapper = new AttributeMapper<>() {
 150                     @Override
 151                     public String name() {
 152                         return name.stringValue();
 153                     }
 154 
 155                     @Override
 156                     public UnknownAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) {
 157                         // Will never get called
 158                         throw new UnsupportedOperationException();
 159                     }
 160 
 161                     @Override
 162                     public void writeAttribute(BufWriter buf, UnknownAttribute attr) {
 163                         buf.writeIndex(name);
 164                         var cont = attr.contents();
 165                         buf.writeInt(cont.length);
 166                         buf.writeBytes(cont);
 167                     }
 168 
 169                     @Override
 170                     public boolean allowMultiple() {
 171                         return true;
 172                     }
 173 
 174                     @Override
 175                     public AttributeMapper.AttributeStability stability() {
 176                         return AttributeStability.UNKNOWN;
 177                     }
 178                 };
 179                 filled.add(new BoundUnknownAttribute(reader, fakeMapper, p));
 180             }
 181             p += len;
 182         }
 183         return Collections.unmodifiableList(filled);
 184     }
 185 
 186     public static final class BoundUnknownAttribute extends BoundAttribute<UnknownAttribute>
 187             implements UnknownAttribute {
 188         public BoundUnknownAttribute(ClassReader cf, AttributeMapper<UnknownAttribute> mapper, int pos) {
 189             super(cf, mapper, pos);
 190         }
 191     }
 192 
 193     public static final class BoundStackMapTableAttribute
 194             extends BoundAttribute<StackMapTableAttribute>
 195             implements StackMapTableAttribute {
 196         final MethodModel method;
 197         final LabelContext ctx;
 198         List<StackMapFrameInfo> entries = null;
 199 
 200         public BoundStackMapTableAttribute(CodeImpl code, ClassReader cf, AttributeMapper<StackMapTableAttribute> mapper, int pos) {
 201             super(cf, mapper, pos);
 202             method = code.parent().orElseThrow();
 203             ctx = code;
 204         }
 205 
 206         @Override
 207         public List<StackMapFrameInfo> entries() {
 208             if (entries == null) {
 209                 entries = new StackMapDecoder(classReader, payloadStart, ctx, StackMapDecoder.initFrameLocals(method),
 210                         StackMapDecoder.initFrameUnsets(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             if (enclosing instanceof CodeImpl ci) {
 287                 this.codeAttribute = ci;
 288             } else {
 289                 throw new IllegalArgumentException("Invalid LocalVariableTable attribute location");
 290             }
 291         }
 292 
 293         @Override
 294         public List<LocalVariableInfo> localVariables() {
 295             if (localVars == null) {
 296                 int cnt = classReader.readU2(payloadStart);
 297                 BoundLocalVariable[] elements = new BoundLocalVariable[cnt];
 298                 int p = payloadStart + 2;
 299                 int pEnd = p + (cnt * 10);
 300                 for (int i = 0; p < pEnd; p += 10, i++) {
 301                     elements[i] = new BoundLocalVariable(codeAttribute, p);
 302                 }
 303                 localVars = List.of(elements);
 304             }
 305             return localVars;
 306         }
 307     }
 308 
 309     public static final class BoundLocalVariableTypeTableAttribute
 310             extends BoundAttribute<LocalVariableTypeTableAttribute>
 311             implements LocalVariableTypeTableAttribute {
 312         private final CodeImpl codeAttribute;
 313         private List<LocalVariableTypeInfo> localVars = null;
 314 
 315         public BoundLocalVariableTypeTableAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<LocalVariableTypeTableAttribute> mapper, int pos) {
 316             super(cf, mapper, pos);
 317             if (enclosing instanceof CodeImpl ci) {
 318                 this.codeAttribute = ci;
 319             } else {
 320                 throw new IllegalArgumentException("Invalid LocalVariableTypeTable attribute location");
 321             }
 322         }
 323 
 324         @Override
 325         public List<LocalVariableTypeInfo> localVariableTypes() {
 326             if (localVars == null) {
 327                 final int cnt = classReader.readU2(payloadStart);
 328                 BoundLocalVariableType[] elements = new BoundLocalVariableType[cnt];
 329                 int p = payloadStart + 2;
 330                 int pEnd = p + (cnt * 10);
 331                 for (int i = 0; p < pEnd; p += 10, i++) {
 332                     elements[i] = new BoundLocalVariableType(codeAttribute, p);
 333                 }
 334                 localVars = List.of(elements);
 335             }
 336             return localVars;
 337         }
 338     }
 339 
 340     public static final class BoundMethodParametersAttribute extends BoundAttribute<MethodParametersAttribute>
 341             implements MethodParametersAttribute {
 342         private List<MethodParameterInfo> parameters = null;
 343 
 344         public BoundMethodParametersAttribute(ClassReader cf, AttributeMapper<MethodParametersAttribute> mapper, int pos) {
 345             super(cf, mapper, pos);
 346         }
 347 
 348         @Override
 349         public List<MethodParameterInfo> parameters() {
 350             if (parameters == null) {
 351                 final int cnt = classReader.readU1(payloadStart);
 352                 MethodParameterInfo[] elements = new MethodParameterInfo[cnt];
 353                 int p = payloadStart + 1;
 354                 int pEnd = p + (cnt * 4);
 355                 for (int i = 0; p < pEnd; p += 4, i++) {
 356                     Utf8Entry name = classReader.readEntryOrNull(p, Utf8Entry.class);
 357                     int accessFlags = classReader.readU2(p + 2);
 358                     elements[i] = MethodParameterInfo.of(Optional.ofNullable(name), accessFlags);
 359                 }
 360                 parameters = List.of(elements);
 361             }
 362             return parameters;
 363         }
 364     }
 365 
 366     public static final class BoundModuleHashesAttribute extends BoundAttribute<ModuleHashesAttribute>
 367             implements ModuleHashesAttribute {
 368         private List<ModuleHashInfo> hashes = null;
 369 
 370         public BoundModuleHashesAttribute(ClassReader cf, AttributeMapper<ModuleHashesAttribute> mapper, int pos) {
 371             super(cf, mapper, pos);
 372         }
 373 
 374         @Override
 375         public Utf8Entry algorithm() {
 376             return classReader.readEntry(payloadStart, Utf8Entry.class);
 377         }
 378 
 379         @Override
 380         public List<ModuleHashInfo> hashes() {
 381             if (hashes == null) {
 382                 final int cnt = classReader.readU2(payloadStart + 2);
 383                 ModuleHashInfo[] elements = new ModuleHashInfo[cnt];
 384                 int p = payloadStart + 4;
 385                 //System.err.printf("%5d: ModuleHashesAttr alg = %s, cnt = %d%n", pos, algorithm(), cnt);
 386                 for (int i = 0; i < cnt; ++i) {
 387                     ModuleEntry module = classReader.readEntry(p, ModuleEntry.class);
 388                     int hashLength = classReader.readU2(p + 2);
 389                     //System.err.printf("%5d:     [%d] module = %s, hashLength = %d%n", p, i, module, hashLength);
 390                     p += 4;
 391                     elements[i] = ModuleHashInfo.of(module, classReader.readBytes(p, hashLength));
 392                     p += hashLength;
 393                 }
 394                 hashes = List.of(elements);
 395             }
 396             return hashes;
 397         }
 398     }
 399 
 400     public static final class BoundRecordAttribute extends BoundAttribute<RecordAttribute>
 401             implements RecordAttribute {
 402         private List<RecordComponentInfo> components = null;
 403 
 404         public BoundRecordAttribute(ClassReader cf, AttributeMapper<RecordAttribute> mapper, int pos) {
 405             super(cf, mapper, pos);
 406         }
 407 
 408         @Override
 409         public List<RecordComponentInfo> components() {
 410             if (components == null) {
 411                 final int cnt = classReader.readU2(payloadStart);
 412                 RecordComponentInfo[] elements = new RecordComponentInfo[cnt];
 413                 int p = payloadStart + 2;
 414                 for (int i = 0; i < cnt; i++) {
 415                     elements[i] = new BoundRecordComponentInfo(classReader, p);
 416                     p = classReader.skipAttributeHolder(p + 4);
 417                 }
 418                 components = List.of(elements);
 419             }
 420             return components;
 421         }
 422     }
 423 
 424     public static final class BoundDeprecatedAttribute extends BoundAttribute<DeprecatedAttribute>
 425             implements DeprecatedAttribute {
 426         public BoundDeprecatedAttribute(ClassReader cf, AttributeMapper<DeprecatedAttribute> mapper, int pos) {
 427             super(cf, mapper, pos);
 428         }
 429     }
 430 
 431     public static final class BoundSignatureAttribute extends BoundAttribute<SignatureAttribute>
 432             implements SignatureAttribute {
 433         public BoundSignatureAttribute(ClassReader cf, AttributeMapper<SignatureAttribute> mapper, int pos) {
 434             super(cf, mapper, pos);
 435         }
 436 
 437         @Override
 438         public Utf8Entry signature() {
 439             return classReader.readEntry(payloadStart, Utf8Entry.class);
 440         }
 441     }
 442 
 443     public static final class BoundSourceFileAttribute extends BoundAttribute<SourceFileAttribute>
 444             implements SourceFileAttribute {
 445         public BoundSourceFileAttribute(ClassReader cf, AttributeMapper<SourceFileAttribute> mapper, int pos) {
 446             super(cf, mapper, pos);
 447         }
 448 
 449         @Override
 450         public Utf8Entry sourceFile() {
 451             return classReader.readEntry(payloadStart, Utf8Entry.class);
 452         }
 453 
 454     }
 455 
 456     public static final class BoundModuleMainClassAttribute extends BoundAttribute<ModuleMainClassAttribute> implements ModuleMainClassAttribute {
 457         public BoundModuleMainClassAttribute(ClassReader cf, AttributeMapper<ModuleMainClassAttribute> mapper, int pos) {
 458             super(cf, mapper, pos);
 459         }
 460 
 461         @Override
 462         public ClassEntry mainClass() {
 463             return classReader.readEntry(payloadStart, ClassEntry.class);
 464         }
 465     }
 466 
 467     public static final class BoundNestHostAttribute extends BoundAttribute<NestHostAttribute>
 468             implements NestHostAttribute {
 469         public BoundNestHostAttribute(ClassReader cf, AttributeMapper<NestHostAttribute> mapper, int pos) {
 470             super(cf, mapper, pos);
 471         }
 472 
 473         @Override
 474         public ClassEntry nestHost() {
 475             return classReader.readEntry(payloadStart, ClassEntry.class);
 476         }
 477     }
 478 
 479     public static final class BoundSourceDebugExtensionAttribute extends BoundAttribute<SourceDebugExtensionAttribute>
 480             implements SourceDebugExtensionAttribute {
 481         public BoundSourceDebugExtensionAttribute(ClassReader cf, AttributeMapper<SourceDebugExtensionAttribute> mapper, int pos) {
 482             super(cf, mapper, pos);
 483         }
 484     }
 485 
 486     public static final class BoundConstantValueAttribute extends BoundAttribute<ConstantValueAttribute>
 487             implements ConstantValueAttribute {
 488         public BoundConstantValueAttribute(ClassReader cf, AttributeMapper<ConstantValueAttribute> mapper, int pos) {
 489             super(cf, mapper, pos);
 490         }
 491 
 492         @Override
 493         public ConstantValueEntry constant() {
 494             return classReader.readEntry(payloadStart, ConstantValueEntry.class);
 495         }
 496 
 497     }
 498 
 499     public static final class BoundModuleTargetAttribute extends BoundAttribute<ModuleTargetAttribute>
 500             implements ModuleTargetAttribute {
 501         public BoundModuleTargetAttribute(ClassReader cf, AttributeMapper<ModuleTargetAttribute> mapper, int pos) {
 502             super(cf, mapper, pos);
 503         }
 504 
 505         @Override
 506         public Utf8Entry targetPlatform() {
 507             return classReader.readEntry(payloadStart, Utf8Entry.class);
 508         }
 509     }
 510 
 511     public static final class BoundCompilationIDAttribute extends BoundAttribute<CompilationIDAttribute>
 512             implements CompilationIDAttribute {
 513         public BoundCompilationIDAttribute(ClassReader cf, AttributeMapper<CompilationIDAttribute> mapper, int pos) {
 514             super(cf, mapper, pos);
 515         }
 516 
 517         @Override
 518         public Utf8Entry compilationId() {
 519             return classReader.readEntry(payloadStart, Utf8Entry.class);
 520         }
 521     }
 522 
 523     public static final class BoundSourceIDAttribute extends BoundAttribute<SourceIDAttribute>
 524             implements SourceIDAttribute {
 525         public BoundSourceIDAttribute(ClassReader cf, AttributeMapper<SourceIDAttribute> mapper, int pos) {
 526             super(cf, mapper, pos);
 527         }
 528 
 529         @Override
 530         public Utf8Entry sourceId() {
 531             return classReader.readEntry(payloadStart, Utf8Entry.class);
 532         }
 533     }
 534 
 535     public static final class BoundModuleResolutionAttribute extends BoundAttribute<ModuleResolutionAttribute>
 536             implements ModuleResolutionAttribute {
 537         public BoundModuleResolutionAttribute(ClassReader cf, AttributeMapper<ModuleResolutionAttribute> mapper, int pos) {
 538             super(cf, mapper, pos);
 539         }
 540 
 541         @Override
 542         public int resolutionFlags() {
 543             return classReader.readU2(payloadStart);
 544         }
 545     }
 546 
 547     public static final class BoundExceptionsAttribute extends BoundAttribute<ExceptionsAttribute>
 548             implements ExceptionsAttribute {
 549         private List<ClassEntry> exceptions = null;
 550 
 551         public BoundExceptionsAttribute(ClassReader cf, AttributeMapper<ExceptionsAttribute> mapper, int pos) {
 552             super(cf, mapper, pos);
 553         }
 554 
 555         @Override
 556         public List<ClassEntry> exceptions() {
 557             if (exceptions == null) {
 558                 exceptions = readEntryList(payloadStart, ClassEntry.class);
 559             }
 560             return exceptions;
 561         }
 562     }
 563 
 564     public static final class BoundModuleAttribute extends BoundAttribute<ModuleAttribute>
 565             implements ModuleAttribute {
 566         private List<ModuleRequireInfo> requires = null;
 567         private List<ModuleExportInfo> exports = null;
 568         private List<ModuleOpenInfo> opens = null;
 569         private List<ClassEntry> uses = null;
 570         private List<ModuleProvideInfo> provides = null;
 571 
 572         public BoundModuleAttribute(ClassReader cf, AttributeMapper<ModuleAttribute> mapper, int pos) {
 573             super(cf, mapper, pos);
 574         }
 575 
 576         @Override
 577         public ModuleEntry moduleName() {
 578             return classReader.readEntry(payloadStart, ModuleEntry.class);
 579         }
 580 
 581         @Override
 582         public int moduleFlagsMask() {
 583             return classReader.readU2(payloadStart + 2);
 584         }
 585 
 586         @Override
 587         public Optional<Utf8Entry> moduleVersion() {
 588             return Optional.ofNullable(classReader.readEntryOrNull(payloadStart + 4, Utf8Entry.class));
 589         }
 590 
 591         @Override
 592         public List<ModuleRequireInfo> requires() {
 593             if (requires == null) {
 594                 structure();
 595             }
 596             return requires;
 597         }
 598 
 599         @Override
 600         public List<ModuleExportInfo> exports() {
 601             if (exports == null) {
 602                 structure();
 603             }
 604             return exports;
 605         }
 606 
 607         @Override
 608         public List<ModuleOpenInfo> opens() {
 609             if (opens == null) {
 610                 structure();
 611             }
 612             return opens;
 613         }
 614 
 615         @Override
 616         public List<ClassEntry> uses() {
 617             if (uses == null) {
 618                 structure();
 619             }
 620             return uses;
 621         }
 622 
 623         @Override
 624         public List<ModuleProvideInfo> provides() {
 625             if (provides == null) {
 626                 structure();
 627             }
 628             return provides;
 629         }
 630 
 631         private void structure() {
 632             int p = payloadStart + 8;
 633 
 634             {
 635                 int cnt = classReader.readU2(payloadStart + 6);
 636                 ModuleRequireInfo[] elements = new ModuleRequireInfo[cnt];
 637                 int end = p + (cnt * 6);
 638                 for (int i = 0; p < end; p += 6, i++) {
 639                     elements[i] = ModuleRequireInfo.of(classReader.readEntry(p, ModuleEntry.class),
 640                             classReader.readU2(p + 2),
 641                             classReader.readEntryOrNull(p + 4, Utf8Entry.class));
 642                 }
 643                 requires = List.of(elements);
 644             }
 645 
 646             {
 647                 int cnt = classReader.readU2(p);
 648                 p += 2;
 649                 ModuleExportInfo[] elements = new ModuleExportInfo[cnt];
 650                 for (int i = 0; i < cnt; i++) {
 651                     PackageEntry pe = classReader.readEntry(p, PackageEntry.class);
 652                     int exportFlags = classReader.readU2(p + 2);
 653                     p += 4;
 654                     List<ModuleEntry> exportsTo = readEntryList(p, ModuleEntry.class);
 655                     p += 2 + exportsTo.size() * 2;
 656                     elements[i] = ModuleExportInfo.of(pe, exportFlags, exportsTo);
 657                 }
 658                 exports = List.of(elements);
 659             }
 660 
 661             {
 662                 int cnt = classReader.readU2(p);
 663                 p += 2;
 664                 ModuleOpenInfo[] elements = new ModuleOpenInfo[cnt];
 665                 for (int i = 0; i < cnt; i++) {
 666                     PackageEntry po = classReader.readEntry(p, PackageEntry.class);
 667                     int opensFlags = classReader.readU2(p + 2);
 668                     p += 4;
 669                     List<ModuleEntry> opensTo = readEntryList(p, ModuleEntry.class);
 670                     p += 2 + opensTo.size() * 2;
 671                     elements[i] = ModuleOpenInfo.of(po, opensFlags, opensTo);
 672                 }
 673                 opens = List.of(elements);
 674             }
 675 
 676             {
 677                 uses = readEntryList(p, ClassEntry.class);
 678                 p += 2 + uses.size() * 2;
 679                 int cnt = classReader.readU2(p);
 680                 p += 2;
 681                 ModuleProvideInfo[] elements = new ModuleProvideInfo[cnt];
 682                 provides = new ArrayList<>(cnt);
 683                 for (int i = 0; i < cnt; i++) {
 684                     ClassEntry c = classReader.readEntry(p, ClassEntry.class);
 685                     p += 2;
 686                     List<ClassEntry> providesWith = readEntryList(p, ClassEntry.class);
 687                     p += 2 + providesWith.size() * 2;
 688                     elements[i] = ModuleProvideInfo.of(c, providesWith);
 689                 }
 690                 provides = List.of(elements);
 691             }
 692         }
 693     }
 694 
 695     public static final class BoundModulePackagesAttribute extends BoundAttribute<ModulePackagesAttribute>
 696             implements ModulePackagesAttribute {
 697         private List<PackageEntry> packages = null;
 698 
 699         public BoundModulePackagesAttribute(ClassReader cf, AttributeMapper<ModulePackagesAttribute> mapper, int pos) {
 700             super(cf, mapper, pos);
 701         }
 702 
 703         @Override
 704         public List<PackageEntry> packages() {
 705             if (packages == null) {
 706                 packages = readEntryList(payloadStart, PackageEntry.class);
 707             }
 708             return packages;
 709         }
 710     }
 711 
 712     public static final class BoundNestMembersAttribute extends BoundAttribute<NestMembersAttribute>
 713             implements NestMembersAttribute {
 714 
 715         private List<ClassEntry> members = null;
 716 
 717         public BoundNestMembersAttribute(ClassReader cf, AttributeMapper<NestMembersAttribute> mapper, int pos) {
 718             super(cf, mapper, pos);
 719         }
 720 
 721         @Override
 722         public List<ClassEntry> nestMembers() {
 723             if (members == null) {
 724                 members = readEntryList(payloadStart, ClassEntry.class);
 725             }
 726             return members;
 727         }
 728     }
 729 
 730     public static final class BoundBootstrapMethodsAttribute extends BoundAttribute<BootstrapMethodsAttribute>
 731             implements BootstrapMethodsAttribute {
 732 
 733         private List<BootstrapMethodEntry> bootstraps = null;
 734         private final int size;
 735 
 736         public BoundBootstrapMethodsAttribute(ClassReader reader, AttributeMapper<BootstrapMethodsAttribute> mapper, int pos) {
 737             super(reader, mapper, pos);
 738             size = classReader.readU2(pos);
 739         }
 740 
 741         @Override
 742         public int bootstrapMethodsSize() {
 743             return size;
 744         }
 745 
 746         @Override
 747         public List<BootstrapMethodEntry> bootstrapMethods() {
 748             if (bootstraps == null) {
 749                 BootstrapMethodEntry[] bs = new BootstrapMethodEntry[size];
 750                 int p = payloadStart + 2;
 751                 for (int i = 0; i < size; ++i) {
 752                     final var handle = classReader.readEntry(p, AbstractPoolEntry.MethodHandleEntryImpl.class);
 753                     final List<LoadableConstantEntry> args = readEntryList(p + 2, LoadableConstantEntry.class);
 754                     p += 4 + args.size() * 2;
 755                     int hash = BootstrapMethodEntryImpl.computeHashCode(handle, args);
 756                     bs[i] = new BootstrapMethodEntryImpl(classReader, i, hash, handle, args);
 757                 }
 758                 bootstraps = List.of(bs);
 759             }
 760             return bootstraps;
 761         }
 762     }
 763 
 764     public static final class BoundInnerClassesAttribute extends BoundAttribute<InnerClassesAttribute>
 765             implements InnerClassesAttribute {
 766         private List<InnerClassInfo> classes;
 767 
 768         public BoundInnerClassesAttribute(ClassReader cf, AttributeMapper<InnerClassesAttribute> mapper, int pos) {
 769             super(cf, mapper, pos);
 770         }
 771 
 772         @Override
 773         public List<InnerClassInfo> classes() {
 774             if (classes == null) {
 775                 final int cnt = classReader.readU2(payloadStart);
 776                 int p = payloadStart + 2;
 777                 InnerClassInfo[] elements = new InnerClassInfo[cnt];
 778                 for (int i = 0; i < cnt; i++) {
 779                     ClassEntry innerClass = classReader.readEntry(p, ClassEntry.class);
 780                     var outerClass = classReader.readEntryOrNull(p + 2, ClassEntry.class);
 781                     var innerName = classReader.readEntryOrNull(p + 4, Utf8Entry.class);
 782                     int flags = classReader.readU2(p + 6);
 783                     p += 8;
 784                     elements[i] = InnerClassInfo.of(innerClass, Optional.ofNullable(outerClass), Optional.ofNullable(innerName), flags);
 785                 }
 786                 classes = List.of(elements);
 787             }
 788             return classes;
 789         }
 790     }
 791 
 792     public static final class BoundEnclosingMethodAttribute extends BoundAttribute<EnclosingMethodAttribute>
 793             implements EnclosingMethodAttribute {
 794         public BoundEnclosingMethodAttribute(ClassReader cf, AttributeMapper<EnclosingMethodAttribute> mapper, int pos) {
 795             super(cf, mapper, pos);
 796         }
 797 
 798         @Override
 799         public ClassEntry enclosingClass() {
 800             return classReader.readEntry(payloadStart, ClassEntry.class);
 801         }
 802 
 803         @Override
 804         public Optional<NameAndTypeEntry> enclosingMethod() {
 805             return Optional.ofNullable(classReader.readEntryOrNull(payloadStart + 2, NameAndTypeEntry.class));
 806         }
 807     }
 808 
 809     public static final class BoundAnnotationDefaultAttr
 810             extends BoundAttribute<AnnotationDefaultAttribute>
 811             implements AnnotationDefaultAttribute {
 812         private AnnotationValue annotationValue;
 813 
 814         public BoundAnnotationDefaultAttr(ClassReader cf, AttributeMapper<AnnotationDefaultAttribute> mapper, int pos) {
 815             super(cf, mapper, pos);
 816         }
 817 
 818         @Override
 819         public AnnotationValue defaultValue() {
 820             if (annotationValue == null)
 821                 annotationValue = AnnotationReader.readElementValue(classReader, payloadStart);
 822             return annotationValue;
 823         }
 824     }
 825 
 826     public static final class BoundRuntimeVisibleTypeAnnotationsAttribute extends BoundAttribute<RuntimeVisibleTypeAnnotationsAttribute>
 827             implements RuntimeVisibleTypeAnnotationsAttribute {
 828 
 829         private final LabelContext labelContext;
 830 
 831         public BoundRuntimeVisibleTypeAnnotationsAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<RuntimeVisibleTypeAnnotationsAttribute> mapper, int pos) {
 832             super(cf, mapper, pos);
 833             this.labelContext = (enclosing instanceof LabelContext lc) ? lc : null;
 834         }
 835 
 836         @Override
 837         public List<TypeAnnotation> annotations() {
 838             return AnnotationReader.readTypeAnnotations(classReader, payloadStart, labelContext);
 839         }
 840     }
 841 
 842     public static final class BoundRuntimeInvisibleTypeAnnotationsAttribute
 843             extends BoundAttribute<RuntimeInvisibleTypeAnnotationsAttribute>
 844             implements RuntimeInvisibleTypeAnnotationsAttribute {
 845         public BoundRuntimeInvisibleTypeAnnotationsAttribute(AttributedElement enclosing, ClassReader cf, AttributeMapper<RuntimeInvisibleTypeAnnotationsAttribute> mapper, int pos) {
 846             super(cf, mapper, pos);
 847             this.labelContext = (enclosing instanceof LabelContext lc) ? lc : null;
 848         }
 849 
 850         private final LabelContext labelContext;
 851 
 852         @Override
 853         public List<TypeAnnotation> annotations() {
 854             return AnnotationReader.readTypeAnnotations(classReader, payloadStart, labelContext);
 855         }
 856     }
 857 
 858     public static final class BoundRuntimeVisibleParameterAnnotationsAttribute
 859             extends BoundAttribute<RuntimeVisibleParameterAnnotationsAttribute>
 860             implements RuntimeVisibleParameterAnnotationsAttribute {
 861 
 862         public BoundRuntimeVisibleParameterAnnotationsAttribute(ClassReader cf, AttributeMapper<RuntimeVisibleParameterAnnotationsAttribute> mapper, int pos) {
 863             super(cf, mapper, pos);
 864         }
 865 
 866         @Override
 867         public List<List<Annotation>> parameterAnnotations() {
 868             return AnnotationReader.readParameterAnnotations(classReader, payloadStart);
 869         }
 870     }
 871 
 872     public static final class BoundRuntimeInvisibleParameterAnnotationsAttribute
 873             extends BoundAttribute<RuntimeInvisibleParameterAnnotationsAttribute>
 874             implements RuntimeInvisibleParameterAnnotationsAttribute {
 875 
 876         public BoundRuntimeInvisibleParameterAnnotationsAttribute(ClassReader cf, AttributeMapper<RuntimeInvisibleParameterAnnotationsAttribute> mapper, int pos) {
 877             super(cf, mapper, pos);
 878         }
 879 
 880         @Override
 881         public List<List<Annotation>> parameterAnnotations() {
 882             return AnnotationReader.readParameterAnnotations(classReader, payloadStart);
 883         }
 884     }
 885 
 886     public static final class BoundRuntimeInvisibleAnnotationsAttribute
 887             extends BoundAttribute<RuntimeInvisibleAnnotationsAttribute>
 888             implements RuntimeInvisibleAnnotationsAttribute {
 889         private List<Annotation> inflated;
 890 
 891         public BoundRuntimeInvisibleAnnotationsAttribute(ClassReader cf,
 892                                                          int payloadStart) {
 893             super(cf, Attributes.runtimeInvisibleAnnotations(), payloadStart);
 894         }
 895 
 896         @Override
 897         public List<Annotation> annotations() {
 898             if (inflated == null)
 899                 inflated = AnnotationReader.readAnnotations(classReader, payloadStart);
 900             return inflated;
 901         }
 902     }
 903 
 904     public static final class BoundRuntimeVisibleAnnotationsAttribute
 905             extends BoundAttribute<RuntimeVisibleAnnotationsAttribute>
 906             implements RuntimeVisibleAnnotationsAttribute {
 907         private List<Annotation> inflated;
 908 
 909         public BoundRuntimeVisibleAnnotationsAttribute(ClassReader cf,
 910                                                        int payloadStart) {
 911             super(cf, Attributes.runtimeVisibleAnnotations(), payloadStart);
 912         }
 913 
 914         @Override
 915         public List<Annotation> annotations() {
 916             if (inflated == null)
 917                 inflated = AnnotationReader.readAnnotations(classReader, payloadStart);
 918             return inflated;
 919         }
 920     }
 921 
 922     public static final class BoundPermittedSubclassesAttribute extends BoundAttribute<PermittedSubclassesAttribute>
 923             implements PermittedSubclassesAttribute {
 924         private List<ClassEntry> permittedSubclasses = null;
 925 
 926         public BoundPermittedSubclassesAttribute(ClassReader cf, AttributeMapper<PermittedSubclassesAttribute> mapper, int pos) {
 927             super(cf, mapper, pos);
 928         }
 929 
 930         @Override
 931         public List<ClassEntry> permittedSubclasses() {
 932             if (permittedSubclasses == null) {
 933                 permittedSubclasses = readEntryList(payloadStart, ClassEntry.class);
 934             }
 935             return permittedSubclasses;
 936         }
 937     }
 938 
 939     public static final class BoundLoadableDescriptorsAttribute extends BoundAttribute<LoadableDescriptorsAttribute>
 940             implements LoadableDescriptorsAttribute {
 941         private List<Utf8Entry> loadableDescriptors = null;
 942 
 943         public BoundLoadableDescriptorsAttribute(ClassReader cf, AttributeMapper<LoadableDescriptorsAttribute> mapper, int pos) {
 944             super(cf, mapper, pos);
 945         }
 946 
 947         @Override
 948         public List<Utf8Entry> loadableDescriptors() {
 949             if (loadableDescriptors == null) {
 950                 loadableDescriptors = readEntryList(payloadStart, Utf8Entry.class);
 951             }
 952             return loadableDescriptors;
 953         }
 954     }
 955 
 956     public abstract static sealed class BoundCodeAttribute
 957             extends BoundAttribute<CodeAttribute>
 958             implements CodeAttribute
 959             permits CodeImpl {
 960         protected final int codeStart;
 961         protected final int codeLength;
 962         protected final int codeEnd;
 963         protected final int attributePos;
 964         protected final int exceptionHandlerPos;
 965         protected final int exceptionHandlerCnt;
 966         protected final MethodModel enclosingMethod;
 967 
 968         public BoundCodeAttribute(AttributedElement enclosing,
 969                                   ClassReader reader,
 970                                   AttributeMapper<CodeAttribute> mapper,
 971                                   int payloadStart) {
 972             super(reader, mapper, payloadStart);
 973             this.codeLength = classReader.readInt(payloadStart + 4);
 974             this.enclosingMethod = (MethodModel) enclosing;
 975             this.codeStart = payloadStart + 8;
 976             this.codeEnd = codeStart + codeLength;
 977             this.exceptionHandlerPos = codeEnd;
 978             this.exceptionHandlerCnt = classReader.readU2(exceptionHandlerPos);
 979             this.attributePos = exceptionHandlerPos + 2 + exceptionHandlerCnt * 8;
 980         }
 981 
 982         // CodeAttribute
 983 
 984         @Override
 985         public int maxStack() {
 986             return classReader.readU2(payloadStart);
 987         }
 988 
 989         @Override
 990         public int maxLocals() {
 991             return classReader.readU2(payloadStart + 2);
 992         }
 993 
 994         @Override
 995         public int codeLength() {
 996             return codeLength;
 997         }
 998 
 999         @Override
1000         public byte[] codeArray() {
1001             return classReader.readBytes(payloadStart + 8, codeLength());
1002         }
1003     }
1004 
1005     /**
1006      * {@return the attribute mapper for a standard attribute}
1007      *
1008      * @param name the name of the attribute to find
1009      */
1010     public static AttributeMapper<?> standardAttribute(Utf8Entry name) {
1011         // critical bootstrap path, so no lambdas nor method handles here
1012         return switch (name.hashCode()) {
1013             case 0x46699ff2 ->
1014                 name.equalsString(NAME_ANNOTATION_DEFAULT) ? annotationDefault() : null;
1015             case 0x5208e184 ->
1016                 name.equalsString(NAME_BOOTSTRAP_METHODS) ? bootstrapMethods() : null;
1017             case 0xcb60907a ->
1018                 name.equalsString(NAME_CHARACTER_RANGE_TABLE) ? characterRangeTable() : null;
1019             case 0x4020220d ->
1020                 name.equalsString(NAME_CODE) ? code() : null;
1021             case 0xc20dd1fe ->
1022                 name.equalsString(NAME_COMPILATION_ID) ? compilationId() : null;
1023             case 0xcab1940d ->
1024                 name.equalsString(NAME_CONSTANT_VALUE) ? constantValue() : null;
1025             case 0x558641d3 ->
1026                 name.equalsString(NAME_DEPRECATED) ? deprecated() : null;
1027             case 0x51d443cd ->
1028                 name.equalsString(NAME_ENCLOSING_METHOD) ? enclosingMethod() : null;
1029             case 0x687c1624 ->
1030                 name.equalsString(NAME_EXCEPTIONS) ? exceptions() : null;
1031             case 0x7adb2910 ->
1032                 name.equalsString(NAME_INNER_CLASSES) ? innerClasses() : null;
1033             case 0x653f0551 ->
1034                 name.equalsString(NAME_LINE_NUMBER_TABLE) ? lineNumberTable() : null;
1035            case 0x5f348b64 ->
1036                 name.equalsString(NAME_LOADABLE_DESCRIPTORS) ? loadableDescriptors() : null;
1037             case 0x64c75927 ->
1038                 name.equalsString(NAME_LOCAL_VARIABLE_TABLE) ? localVariableTable() : null;
1039             case 0x6697f98d ->
1040                 name.equalsString(NAME_LOCAL_VARIABLE_TYPE_TABLE) ? localVariableTypeTable() : null;
1041             case 0xdbb0cdcb ->
1042                 name.equalsString(NAME_METHOD_PARAMETERS) ? methodParameters() : null;
1043             case 0xc9b0928c ->
1044                 name.equalsString(NAME_MODULE) ? module() : null;
1045             case 0x41cd27e8 ->
1046                 name.equalsString(NAME_MODULE_HASHES) ? moduleHashes() : null;
1047             case 0x7deb0a13 ->
1048                 name.equalsString(NAME_MODULE_MAIN_CLASS) ? moduleMainClass() : null;
1049             case 0x6706ff99 ->
1050                 name.equalsString(NAME_MODULE_PACKAGES) ? modulePackages() : null;
1051             case 0x60272858 ->
1052                 name.equalsString(NAME_MODULE_RESOLUTION) ? moduleResolution() : null;
1053             case 0x5646d73d ->
1054                 name.equalsString(NAME_MODULE_TARGET) ? moduleTarget() : null;
1055             case 0x50336c40 ->
1056                 name.equalsString(NAME_NEST_HOST) ? nestHost() : null;
1057             case 0x4735ab81 ->
1058                 name.equalsString(NAME_NEST_MEMBERS) ? nestMembers() : null;
1059             case 0x7100d9fe ->
1060                 name.equalsString(NAME_PERMITTED_SUBCLASSES) ? permittedSubclasses() : null;
1061             case 0xd1ab5871 ->
1062                 name.equalsString(NAME_RECORD) ? record() : null;
1063             case 0x7588550f ->
1064                 name.equalsString(NAME_RUNTIME_INVISIBLE_ANNOTATIONS) ? runtimeInvisibleAnnotations() : null;
1065             case 0xcc74da30 ->
1066                 name.equalsString(NAME_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS) ? runtimeInvisibleParameterAnnotations() : null;
1067             case 0xf67697f5 ->
1068                 name.equalsString(NAME_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS) ? runtimeInvisibleTypeAnnotations() : null;
1069             case 0xe0837d2a ->
1070                 name.equalsString(NAME_RUNTIME_VISIBLE_ANNOTATIONS) ? runtimeVisibleAnnotations() : null;
1071             case 0xc945a075 ->
1072                 name.equalsString(NAME_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS) ? runtimeVisibleParameterAnnotations() : null;
1073             case 0x611a3a90 ->
1074                 name.equalsString(NAME_RUNTIME_VISIBLE_TYPE_ANNOTATIONS) ? runtimeVisibleTypeAnnotations() : null;
1075             case 0xf76fb898 ->
1076                 name.equalsString(NAME_SIGNATURE) ? signature() : null;
1077             case 0x6b41b047 ->
1078                 name.equalsString(NAME_SOURCE_DEBUG_EXTENSION) ? sourceDebugExtension() : null;
1079             case 0x748c2857 ->
1080                 name.equalsString(NAME_SOURCE_FILE) ? sourceFile() : null;
1081             case 0x6bf13a96 ->
1082                 name.equalsString(NAME_SOURCE_ID) ? sourceId() : null;
1083             case 0xfa85ee5a ->
1084                 name.equalsString(NAME_STACK_MAP_TABLE) ? stackMapTable() : null;
1085             case 0xf2670725 ->
1086                 name.equalsString(NAME_SYNTHETIC) ? synthetic() : null;
1087             default -> null;
1088         };
1089     }
1090 }