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