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