1 /*
   2  * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.  Oracle designates this
   9  * particular file as subject to the "Classpath" exception as provided
  10  * by Oracle in the LICENSE file that accompanied this code.
  11  *
  12  * This code is distributed in the hope that it will be useful, but WITHOUT
  13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15  * version 2 for more details (a copy is included in the LICENSE file that
  16  * accompanied this code).
  17  *
  18  * You should have received a copy of the GNU General Public License version
  19  * 2 along with this work; if not, write to the Free Software Foundation,
  20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  21  *
  22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  23  * or visit www.oracle.com if you need additional information or have any
  24  * questions.
  25  */
  26 package jdk.internal.classfile.impl;
  27 
  28 import java.lang.classfile.*;
  29 import java.lang.classfile.attribute.CodeAttribute;
  30 import java.lang.classfile.attribute.LineNumberTableAttribute;
  31 import java.lang.classfile.constantpool.*;
  32 import java.lang.classfile.instruction.CharacterRange;
  33 import java.lang.classfile.instruction.ExceptionCatch;
  34 import java.lang.classfile.instruction.LocalVariable;
  35 import java.lang.classfile.instruction.LocalVariableType;
  36 import java.lang.classfile.instruction.SwitchCase;
  37 import java.lang.constant.ClassDesc;
  38 import java.lang.constant.MethodTypeDesc;
  39 import java.util.*;
  40 import java.util.function.Consumer;
  41 import java.util.function.Function;
  42 
  43 import static java.util.Objects.requireNonNull;
  44 import static jdk.internal.classfile.impl.BytecodeHelpers.*;
  45 import static jdk.internal.classfile.impl.RawBytecodeHelper.*;
  46 
  47 public final class DirectCodeBuilder
  48         extends AbstractDirectBuilder<CodeModel>
  49         implements TerminalCodeBuilder {
  50     private static final CharacterRange[] EMPTY_CHARACTER_RANGE = {};
  51     private static final LocalVariable[] EMPTY_LOCAL_VARIABLE_ARRAY = {};
  52     private static final LocalVariableType[] EMPTY_LOCAL_VARIABLE_TYPE_ARRAY = {};
  53     private static final DeferredLabel[] EMPTY_DEFERRED_LABEL_ARRAY = {};
  54 
  55     final List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers = new ArrayList<>();
  56     private CharacterRange[] characterRanges = EMPTY_CHARACTER_RANGE;
  57     private LocalVariable[] localVariables = EMPTY_LOCAL_VARIABLE_ARRAY;
  58     private LocalVariableType[] localVariableTypes = EMPTY_LOCAL_VARIABLE_TYPE_ARRAY;
  59     private int characterRangesCount = 0;
  60     private int localVariablesCount = 0;
  61     private int localVariableTypesCount = 0;
  62     private final boolean transformDeferredJumps, transformKnownJumps;
  63     private final Label startLabel, endLabel;
  64     final MethodInfo methodInfo;
  65     final BufWriterImpl bytecodesBufWriter;
  66     private CodeAttribute mruParent;
  67     private int[] mruParentTable;
  68     private Map<CodeAttribute, int[]> parentMap;
  69     private DedupLineNumberTableAttribute lineNumberWriter;
  70     private int topLocal;
  71 
  72     private DeferredLabel[] deferredLabels = EMPTY_DEFERRED_LABEL_ARRAY;
  73     private int deferredLabelsCount = 0;
  74 
  75     private int maxStackHint = -1;
  76     private int maxLocalsHint = -1;
  77 
  78     /* Locals management
  79        lazily computed maxLocal = -1
  80        first time: derive count from methodType descriptor (for new methods) & ACC_STATIC,
  81        or model maxLocals (for transformation)
  82        block builders inherit parent count
  83        allocLocal(TypeKind) bumps by nSlots
  84      */
  85 
  86     public static UnboundAttribute<CodeAttribute> build(MethodInfo methodInfo,
  87                                                  Consumer<? super CodeBuilder> handler,
  88                                                  SplitConstantPool constantPool,
  89                                                  ClassFileImpl context,
  90                                                  CodeModel original) {
  91         DirectCodeBuilder cb;
  92         try {
  93             handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, false));
  94             cb.buildContent();
  95         } catch (LabelOverflowException loe) {
  96             if (context.fixShortJumps()) {
  97                 handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, true));
  98                 cb.buildContent();
  99             }
 100             else
 101                 throw loe;
 102         }
 103         return cb.content;
 104     }
 105 
 106     private DirectCodeBuilder(MethodInfo methodInfo,
 107                               SplitConstantPool constantPool,
 108                               ClassFileImpl context,
 109                               CodeModel original,
 110                               boolean transformDeferredJumps) {
 111         super(constantPool, context);
 112         setOriginal(original);
 113         this.methodInfo = methodInfo;
 114         this.transformDeferredJumps = transformDeferredJumps;
 115         this.transformKnownJumps = context.fixShortJumps();
 116         bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength())
 117                 : new BufWriterImpl(constantPool, context);
 118         this.startLabel = new LabelImpl(this, 0);
 119         this.endLabel = new LabelImpl(this, -1);
 120         this.topLocal = TerminalCodeBuilder.setupTopLocal(methodInfo, original);
 121     }
 122 
 123     @Override
 124     public CodeBuilder with(CodeElement element) {
 125         if (element instanceof AbstractElement ae) {
 126             ae.writeTo(this);
 127         } else {
 128             writeAttribute((CustomAttribute<?>) requireNonNull(element));
 129         }
 130         return this;
 131     }
 132 
 133     @Override
 134     public Label newLabel() {
 135         return new LabelImpl(this, -1);
 136     }
 137 
 138     @Override
 139     public Label startLabel() {
 140         return startLabel;
 141     }
 142 
 143     @Override
 144     public Label endLabel() {
 145         return endLabel;
 146     }
 147 
 148     @Override
 149     public int receiverSlot() {
 150         return methodInfo.receiverSlot();
 151     }
 152 
 153     @Override
 154     public int parameterSlot(int paramNo) {
 155         return methodInfo.parameterSlot(paramNo);
 156     }
 157 
 158     public int curTopLocal() {
 159         return topLocal;
 160     }
 161 
 162     @Override
 163     public int allocateLocal(TypeKind typeKind) {
 164         int retVal = topLocal;
 165         topLocal += typeKind.slotSize();
 166         return retVal;
 167     }
 168 
 169     public int curPc() {
 170         return bytecodesBufWriter.size();
 171     }
 172 
 173     public MethodInfo methodInfo() {
 174         return methodInfo;
 175     }
 176 
 177     public static void withMaxs(CodeBuilder cob, int stacks, int locals) {
 178         var dcb = (DirectCodeBuilder) cob;
 179         dcb.maxStackHint = stacks;
 180         dcb.maxLocalsHint = locals;
 181     }
 182 
 183     private UnboundAttribute<CodeAttribute> content = null;
 184 
 185     private void writeExceptionHandlers(BufWriterImpl buf) {
 186         int pos = buf.size();
 187         int handlersSize = handlers.size();
 188         buf.writeU2(handlersSize);
 189         if (handlersSize > 0) {
 190             writeExceptionHandlers(buf, pos);
 191         }
 192     }
 193 
 194     private void writeExceptionHandlers(BufWriterImpl buf, int pos) {
 195         int handlersSize = handlers.size();
 196         for (AbstractPseudoInstruction.ExceptionCatchImpl h : handlers) {
 197             int startPc = labelToBci(h.tryStart());
 198             int endPc = labelToBci(h.tryEnd());
 199             int handlerPc = labelToBci(h.handler());
 200             if (startPc == -1 || endPc == -1 || handlerPc == -1) {
 201                 if (context.dropDeadLabels()) {
 202                     handlersSize--;
 203                 } else {
 204                     throw new IllegalArgumentException("Unbound label in exception handler");
 205                 }
 206             } else {
 207                 buf.writeU2U2U2(startPc, endPc, handlerPc);
 208                 buf.writeIndexOrZero(h.catchTypeEntry());
 209                 handlersSize++;
 210             }
 211         }
 212         if (handlersSize < handlers.size())
 213             buf.patchU2(pos, handlersSize);
 214     }
 215 
 216     private void buildContent() {
 217         if (content != null) return;
 218         setLabelTarget(endLabel);
 219 
 220         // Backfill branches for which Label didn't have position yet
 221         processDeferredLabels();
 222 
 223         if (context.passDebugElements()) {
 224             if (characterRangesCount > 0) {
 225                 Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) {
 226 
 227                     @Override
 228                     public void writeBody(BufWriterImpl b) {
 229                         int pos = b.size();
 230                         int crSize = characterRangesCount;
 231                         b.writeU2(crSize);
 232                         for (int i = 0; i < characterRangesCount; i++) {
 233                             CharacterRange cr = characterRanges[i];
 234                             var start = labelToBci(cr.startScope());
 235                             var end = labelToBci(cr.endScope());
 236                             if (start == -1 || end == -1) {
 237                                 if (context.dropDeadLabels()) {
 238                                     crSize--;
 239                                 } else {
 240                                     throw new IllegalArgumentException("Unbound label in character range");
 241                                 }
 242                             } else {
 243                                 b.writeU2U2(start, end - 1);
 244                                 b.writeIntInt(cr.characterRangeStart(), cr.characterRangeEnd());
 245                                 b.writeU2(cr.flags());
 246                             }
 247                         }
 248                         if (crSize < characterRangesCount)
 249                             b.patchU2(pos, crSize);
 250                     }
 251 
 252                     @Override
 253                     public Utf8Entry attributeName() {
 254                         return constantPool.utf8Entry(Attributes.NAME_CHARACTER_RANGE_TABLE);
 255                     }
 256                 };
 257                 attributes.withAttribute(a);
 258             }
 259 
 260             if (localVariablesCount > 0) {
 261                 Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTable()) {
 262                     @Override
 263                     public void writeBody(BufWriterImpl b) {
 264                         int pos = b.size();
 265                         int lvSize = localVariablesCount;
 266                         b.writeU2(lvSize);
 267                         for (int i = 0; i < localVariablesCount; i++) {
 268                             LocalVariable l = localVariables[i];
 269                             if (!Util.writeLocalVariable(b, l)) {
 270                                 if (context.dropDeadLabels()) {
 271                                     lvSize--;
 272                                 } else {
 273                                     throw new IllegalArgumentException("Unbound label in local variable type");
 274                                 }
 275                             }
 276                         }
 277                         if (lvSize < localVariablesCount)
 278                             b.patchU2(pos, lvSize);
 279                     }
 280 
 281                     @Override
 282                     public Utf8Entry attributeName() {
 283                         return constantPool.utf8Entry(Attributes.NAME_LOCAL_VARIABLE_TABLE);
 284                     }
 285                 };
 286                 attributes.withAttribute(a);
 287             }
 288 
 289             if (localVariableTypesCount > 0) {
 290                 Attribute<?> a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTypeTable()) {
 291                     @Override
 292                     public void writeBody(BufWriterImpl b) {
 293                         int pos = b.size();
 294                         int lvtSize = localVariableTypesCount;
 295                         b.writeU2(lvtSize);
 296                         for (int i = 0; i < localVariableTypesCount; i++) {
 297                             LocalVariableType l = localVariableTypes[i];
 298                             if (!Util.writeLocalVariable(b, l)) {
 299                                 if (context.dropDeadLabels()) {
 300                                     lvtSize--;
 301                                 } else {
 302                                     throw new IllegalArgumentException("Unbound label in local variable type");
 303                                 }
 304                             }
 305                         }
 306                         if (lvtSize < localVariableTypesCount)
 307                             b.patchU2(pos, lvtSize);
 308                     }
 309 
 310                     @Override
 311                     public Utf8Entry attributeName() {
 312                         return constantPool.utf8Entry(Attributes.NAME_LOCAL_VARIABLE_TYPE_TABLE);
 313                     }
 314                 };
 315                 attributes.withAttribute(a);
 316             }
 317         }
 318 
 319         if (lineNumberWriter != null) {
 320             attributes.withAttribute(lineNumberWriter);
 321         }
 322 
 323         content = new UnboundAttribute.AdHocAttribute<>(Attributes.code()) {
 324 
 325             private void writeCounters(boolean codeMatch, BufWriterImpl buf) {
 326                 if (codeMatch) {
 327                     var originalAttribute = (CodeImpl) original;
 328                     buf.writeU2U2(originalAttribute.maxStack(), originalAttribute.maxLocals());
 329                 } else if (maxLocalsHint >= 0 && maxStackHint >= 0) {
 330                     buf.writeU2U2(maxStackHint, maxLocalsHint);
 331                 } else {
 332                     StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
 333                     buf.writeU2U2(cntr.maxStack(), cntr.maxLocals());
 334                 }
 335             }
 336 
 337             private void generateStackMaps(BufWriterImpl buf) throws IllegalArgumentException {
 338                 //new instance of generator immediately calculates maxStack, maxLocals, all frames,
 339                 // patches dead bytecode blocks and removes them from exception table
 340                 var dcb = DirectCodeBuilder.this;
 341                 StackMapGenerator gen = StackMapGenerator.of(dcb, buf);
 342                 dcb.attributes.withAttribute(gen.stackMapTableAttribute());
 343                 buf.writeU2U2(gen.maxStack(), gen.maxLocals());
 344             }
 345 
 346             private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) {
 347                 if (buf.getMajorVersion() >= ClassFile.JAVA_6_VERSION) {
 348                     try {
 349                         generateStackMaps(buf);
 350                     } catch (IllegalArgumentException e) {
 351                         //failover following JVMS-4.10
 352                         if (buf.getMajorVersion() == ClassFile.JAVA_6_VERSION) {
 353                             writeCounters(codeMatch, buf);
 354                         } else {
 355                             throw e;
 356                         }
 357                     }
 358                 } else {
 359                     writeCounters(codeMatch, buf);
 360                 }
 361             }
 362 
 363             @Override
 364             public void writeBody(BufWriterImpl buf) {
 365                 DirectCodeBuilder dcb = DirectCodeBuilder.this;
 366 
 367                 int codeLength = curPc();
 368                 if (codeLength == 0 || codeLength >= 65536) {
 369                     throw new IllegalArgumentException(String.format(
 370                             "Code length %d is outside the allowed range in %s%s",
 371                             codeLength,
 372                             dcb.methodInfo.methodName().stringValue(),
 373                             dcb.methodInfo.methodTypeSymbol().displayDescriptor()));
 374                 }
 375 
 376                 boolean codeMatch = dcb.original != null && codeAndExceptionsMatch(codeLength);
 377                 buf.setLabelContext(dcb, codeMatch);
 378                 var context = dcb.context;
 379                 if (context.stackMapsWhenRequired()) {
 380                     if (codeMatch) {
 381                         dcb.attributes.withAttribute(dcb.original.findAttribute(Attributes.stackMapTable()).orElse(null));
 382                         writeCounters(true, buf);
 383                     } else {
 384                         tryGenerateStackMaps(false, buf);
 385                     }
 386                 } else if (context.generateStackMaps()) {
 387                     generateStackMaps(buf);
 388                 } else if (context.dropStackMaps()) {
 389                     writeCounters(codeMatch, buf);
 390                 }
 391 
 392                 buf.writeInt(codeLength);
 393                 buf.writeBytes(dcb.bytecodesBufWriter);
 394                 dcb.writeExceptionHandlers(buf);
 395                 dcb.attributes.writeTo(buf);
 396                 buf.setLabelContext(null, false);
 397             }
 398 
 399             @Override
 400             public Utf8Entry attributeName() {
 401                 return constantPool.utf8Entry(Attributes.NAME_CODE);
 402             }
 403         };
 404     }
 405 
 406     private static class DedupLineNumberTableAttribute extends UnboundAttribute.AdHocAttribute<LineNumberTableAttribute> {
 407         private final BufWriterImpl buf;
 408         private int lastPc, lastLine, writtenLine;
 409 
 410         public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool, ClassFileImpl context) {
 411             super(Attributes.lineNumberTable());
 412             buf = new BufWriterImpl(constantPool, context);
 413             lastPc = -1;
 414             writtenLine = -1;
 415         }
 416 
 417         private void push() {
 418             //subsequent identical line numbers are skipped
 419             if (lastPc >= 0 && lastLine != writtenLine) {
 420                 buf.writeU2U2(lastPc, lastLine);
 421                 writtenLine = lastLine;
 422             }
 423         }
 424 
 425         //writes are expected ordered by pc in ascending sequence
 426         public void writeLineNumber(int pc, int lineNo) {
 427             //for each pc only the latest line number is written
 428             if (lastPc != pc && lastLine != lineNo) {
 429                 push();
 430                 lastPc = pc;
 431             }
 432             lastLine = lineNo;
 433         }
 434 
 435         @Override
 436         public void writeBody(BufWriterImpl b) {
 437             throw new UnsupportedOperationException();
 438         }
 439 
 440         @Override
 441         public void writeTo(BufWriterImpl b) {
 442             b.writeIndex(b.constantPool().utf8Entry(Attributes.NAME_LINE_NUMBER_TABLE));
 443             push();
 444             b.writeInt(buf.size() + 2);
 445             b.writeU2(buf.size() / 4);
 446             b.writeBytes(buf);
 447         }
 448 
 449         @Override
 450         public Utf8Entry attributeName() {
 451             return buf.constantPool().utf8Entry(Attributes.NAME_LINE_NUMBER_TABLE);
 452         }
 453     }
 454 
 455     private boolean codeAndExceptionsMatch(int codeLength) {
 456         boolean codeAttributesMatch;
 457         if (original instanceof CodeImpl cai && canWriteDirect(cai.constantPool())) {
 458             codeAttributesMatch = cai.codeLength == curPc()
 459                                   && cai.compareCodeBytes(bytecodesBufWriter, 0, codeLength);
 460             if (codeAttributesMatch) {
 461                 var bw = new BufWriterImpl(constantPool, context);
 462                 writeExceptionHandlers(bw);
 463                 codeAttributesMatch = cai.classReader.compare(bw, 0, cai.exceptionHandlerPos, bw.size());
 464             }
 465         }
 466         else
 467             codeAttributesMatch = false;
 468         return codeAttributesMatch;
 469     }
 470 
 471     // Writing support
 472 
 473     private record DeferredLabel(int labelPc, int size, int instructionPc, Label label) { }
 474 
 475     private void processDeferredLabels() {
 476         for (int i = 0; i < deferredLabelsCount; i++) {
 477             DeferredLabel dl = deferredLabels[i];
 478             int branchOffset = labelToBci(dl.label) - dl.instructionPc;
 479             if (dl.size == 2) {
 480                 if ((short) branchOffset != branchOffset) throw new LabelOverflowException();
 481                 bytecodesBufWriter.patchU2(dl.labelPc, branchOffset);
 482             } else {
 483                 assert dl.size == 4;
 484                 bytecodesBufWriter.patchInt(dl.labelPc, branchOffset);
 485             }
 486         }
 487     }
 488 
 489     // Instruction writing
 490 
 491     public void writeBytecode(Opcode opcode) {
 492         assert !opcode.isWide();
 493         bytecodesBufWriter.writeU1(opcode.bytecode());
 494     }
 495 
 496     // Instruction version, refer to opcode, trusted
 497     public void writeLocalVar(Opcode opcode, int slot) {
 498         if (opcode.isWide()) {
 499             bytecodesBufWriter.writeU2U2(opcode.bytecode(), slot);
 500         } else {
 501             bytecodesBufWriter.writeU1U1(opcode.bytecode(), slot);
 502         }
 503     }
 504 
 505     // local var access, not a trusted write method, needs slot validation
 506     private void localAccess(int bytecode, int slot) {
 507         if ((slot & ~0xFF) == 0) {
 508             bytecodesBufWriter.writeU1U1(bytecode, slot);
 509         } else {
 510             BytecodeHelpers.validateSlot(slot);
 511             bytecodesBufWriter.writeU1U1U2(WIDE, bytecode, slot);
 512         }
 513     }
 514 
 515     public void writeIncrement(boolean wide, int slot, int val) {
 516         if (wide) {
 517             bytecodesBufWriter.writeU2U2U2((WIDE << 8) | IINC, slot, val);
 518         } else {
 519             bytecodesBufWriter.writeU1U1U1(IINC, slot, val);
 520         }
 521     }
 522 
 523     public void writeBranch(Opcode op, Label target) {
 524         if (op.sizeIfFixed() == 3) {
 525             writeShortJump(op.bytecode(), target);
 526         } else {
 527             writeLongJump(op.bytecode(), target);
 528         }
 529     }
 530 
 531     private void writeLongLabelOffset(int instructionPc, Label label) {
 532         int targetBci = labelToBci(label);
 533 
 534         // algebraic union of jump | (instructionPc, target), distinguished by null == target.
 535         int jumpOrInstructionPc;
 536         Label nullOrTarget;
 537         if (targetBci == -1) {
 538             jumpOrInstructionPc = instructionPc;
 539             nullOrTarget = label;
 540         } else {
 541             jumpOrInstructionPc = targetBci - instructionPc;
 542             nullOrTarget = null;
 543         }
 544 
 545         writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget);
 546     }
 547 
 548     private void writeShortJump(int bytecode, Label target) {
 549         int instructionPc = curPc();
 550         int targetBci = labelToBci(target);
 551 
 552         // algebraic union of jump | (instructionPc, target), distinguished by null == target.
 553         int jumpOrInstructionPc;
 554         Label nullOrTarget;
 555         if (targetBci == -1) {
 556             jumpOrInstructionPc = instructionPc;
 557             nullOrTarget = target;
 558         } else {
 559             jumpOrInstructionPc = targetBci - instructionPc;
 560             nullOrTarget = null;
 561         }
 562 
 563         //transform short-opcode forward jumps if enforced, and backward jumps if enabled and overflowing
 564         if (transformDeferredJumps || transformKnownJumps && nullOrTarget == null && jumpOrInstructionPc < Short.MIN_VALUE) {
 565             fixShortJump(bytecode, jumpOrInstructionPc, nullOrTarget);
 566         } else {
 567             bytecodesBufWriter.writeU1(bytecode);
 568             writeParsedShortLabel(jumpOrInstructionPc, nullOrTarget);
 569         }
 570     }
 571 
 572     private void writeLongJump(int bytecode, Label target) {
 573         int instructionPc = curPc();
 574         bytecodesBufWriter.writeU1(bytecode);
 575         writeLongLabelOffset(instructionPc, target);
 576     }
 577 
 578     private void fixShortJump(int bytecode, int jumpOrInstructionPc, Label nullOrTarget) {
 579         if (bytecode == GOTO) {
 580             bytecodesBufWriter.writeU1(GOTO_W);
 581             writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget);
 582         } else if (bytecode == JSR) {
 583             bytecodesBufWriter.writeU1(JSR_W);
 584             writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget);
 585         } else {
 586             bytecodesBufWriter.writeU1U2(
 587                     BytecodeHelpers.reverseBranchOpcode(bytecode),   // u1
 588                     8); // u1 + s2 + u1 + s4                         // s2
 589             bytecodesBufWriter.writeU1(GOTO_W);                      // u1
 590             if (nullOrTarget == null) {
 591                 jumpOrInstructionPc -= 3; // jump -= 3;
 592             } else {
 593                 jumpOrInstructionPc += 3; // instructionPc += 3;
 594             }
 595             writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); // s4
 596         }
 597     }
 598 
 599     private void writeParsedShortLabel(int jumpOrInstructionPc, Label nullOrTarget) {
 600         if (nullOrTarget == null) {
 601             if ((short) jumpOrInstructionPc != jumpOrInstructionPc)
 602                 throw new LabelOverflowException();
 603             bytecodesBufWriter.writeU2(jumpOrInstructionPc);
 604         } else {
 605             int pc = bytecodesBufWriter.skip(2);
 606             addLabel(new DeferredLabel(pc, 2, jumpOrInstructionPc, nullOrTarget));
 607         }
 608     }
 609 
 610     private void writeParsedLongLabel(int jumpOrInstructionPc, Label nullOrTarget) {
 611         if (nullOrTarget == null) {
 612             bytecodesBufWriter.writeInt(jumpOrInstructionPc);
 613         } else {
 614             int pc = bytecodesBufWriter.skip(4);
 615             addLabel(new DeferredLabel(pc, 4, jumpOrInstructionPc, nullOrTarget));
 616         }
 617     }
 618 
 619     public void writeLookupSwitch(Label defaultTarget, List<SwitchCase> cases) {
 620         int instructionPc = curPc();
 621         bytecodesBufWriter.writeU1(LOOKUPSWITCH);
 622         int pad = 4 - (curPc() % 4);
 623         if (pad != 4)
 624             bytecodesBufWriter.skip(pad); // padding content can be anything
 625         writeLongLabelOffset(instructionPc, defaultTarget);
 626         bytecodesBufWriter.writeInt(cases.size());
 627         cases = new ArrayList<>(cases);
 628         cases.sort(new Comparator<>() {
 629             @Override
 630             public int compare(SwitchCase c1, SwitchCase c2) {
 631                 return Integer.compare(c1.caseValue(), c2.caseValue());
 632             }
 633         });
 634         for (var c : cases) {
 635             bytecodesBufWriter.writeInt(c.caseValue());
 636             var target = c.target();
 637             writeLongLabelOffset(instructionPc, target);
 638         }
 639     }
 640 
 641     public void writeTableSwitch(int low, int high, Label defaultTarget, List<SwitchCase> cases) {
 642         int instructionPc = curPc();
 643         bytecodesBufWriter.writeU1(TABLESWITCH);
 644         int pad = 4 - (curPc() % 4);
 645         if (pad != 4)
 646             bytecodesBufWriter.skip(pad); // padding content can be anything
 647         writeLongLabelOffset(instructionPc, defaultTarget);
 648         bytecodesBufWriter.writeIntInt(low, high);
 649         var caseMap = new HashMap<Integer, Label>(cases.size());
 650         for (var c : cases) {
 651             caseMap.put(c.caseValue(), c.target());
 652         }
 653         for (long l = low; l<=high; l++) {
 654             var target = caseMap.getOrDefault((int)l, defaultTarget);
 655             writeLongLabelOffset(instructionPc, target);
 656         }
 657     }
 658 
 659     public void writeFieldAccess(Opcode opcode, FieldRefEntry ref) {
 660         bytecodesBufWriter.writeIndex(opcode.bytecode(), ref);
 661     }
 662 
 663     public void writeInvokeNormal(Opcode opcode, MemberRefEntry ref) {
 664         bytecodesBufWriter.writeIndex(opcode.bytecode(), ref);
 665     }
 666 
 667     public void writeInvokeInterface(Opcode opcode,
 668                                      InterfaceMethodRefEntry ref,
 669                                      int count) {
 670         bytecodesBufWriter.writeIndex(opcode.bytecode(), ref);
 671         bytecodesBufWriter.writeU1U1(count, 0);
 672     }
 673 
 674     public void writeInvokeDynamic(InvokeDynamicEntry ref) {
 675         bytecodesBufWriter.writeU1U2U2(INVOKEDYNAMIC, bytecodesBufWriter.cpIndex(ref), 0);
 676     }
 677 
 678     public void writeNewObject(ClassEntry type) {
 679         bytecodesBufWriter.writeIndex(NEW, type);
 680     }
 681 
 682     public void writeNewPrimitiveArray(int newArrayCode) {
 683         bytecodesBufWriter.writeU1U1(NEWARRAY, newArrayCode);
 684     }
 685 
 686     public void writeNewReferenceArray(ClassEntry type) {
 687         bytecodesBufWriter.writeIndex(ANEWARRAY, type);
 688     }
 689 
 690     public void writeNewMultidimensionalArray(int dimensions, ClassEntry type) {
 691         bytecodesBufWriter.writeIndex(MULTIANEWARRAY, type);
 692         bytecodesBufWriter.writeU1(dimensions);
 693     }
 694 
 695     public void writeTypeCheck(Opcode opcode, ClassEntry type) {
 696         bytecodesBufWriter.writeIndex(opcode.bytecode(), type);
 697     }
 698 
 699     public void writeArgumentConstant(Opcode opcode, int value) {
 700         if (opcode.sizeIfFixed() == 3) {
 701             bytecodesBufWriter.writeU1U2(opcode.bytecode(), value);
 702         } else {
 703             bytecodesBufWriter.writeU1U1(opcode.bytecode(), value);
 704         }
 705     }
 706 
 707     // value may not be writable to this constant pool
 708     public void writeAdaptLoadConstant(Opcode opcode, LoadableConstantEntry value) {
 709         var pe = AbstractPoolEntry.maybeClone(constantPool, value);
 710         int index = pe.index();
 711         if (pe != value && opcode != Opcode.LDC2_W) {
 712             // rewrite ldc/ldc_w if external entry; ldc2_w never needs rewrites
 713             opcode = index <= 0xFF ? Opcode.LDC : Opcode.LDC_W;
 714         }
 715 
 716         writeDirectLoadConstant(opcode, pe);
 717     }
 718 
 719     // the loadable entry is writable to this constant pool
 720     public void writeDirectLoadConstant(Opcode opcode, LoadableConstantEntry pe) {
 721         assert !opcode.isWide() && canWriteDirect(pe.constantPool());
 722         int index = pe.index();
 723         if (opcode.sizeIfFixed() == 3) {
 724             bytecodesBufWriter.writeU1U2(opcode.bytecode(), index);
 725         } else {
 726             bytecodesBufWriter.writeU1U1(opcode.bytecode(), index);
 727         }
 728     }
 729 
 730     @Override
 731     public Label getLabel(int bci) {
 732         throw new UnsupportedOperationException("Lookup by BCI not supported by CodeBuilder");
 733     }
 734 
 735     @Override
 736     public int labelToBci(Label label) {
 737         LabelImpl lab = (LabelImpl) label;
 738         LabelContext context = lab.labelContext();
 739         if (context == this) {
 740             return lab.getBCI();
 741         }
 742         return labelToBci(context, lab);
 743     }
 744 
 745     private int labelToBci(LabelContext context, LabelImpl lab) {
 746         if (context == mruParent) {
 747             return mruParentTable[lab.getBCI()] - 1;
 748         }
 749         else if (context instanceof CodeAttribute parent) {
 750             if (parentMap == null)
 751                 parentMap = new IdentityHashMap<>();
 752             //critical JDK bootstrap path, cannot use lambda here
 753             int[] table = parentMap.computeIfAbsent(parent, new Function<CodeAttribute, int[]>() {
 754                 @Override
 755                 public int[] apply(CodeAttribute x) {
 756                     return new int[parent.codeLength() + 1];
 757                 }
 758             });
 759 
 760             mruParent = parent;
 761             mruParentTable = table;
 762             return mruParentTable[lab.getBCI()] - 1;
 763         }
 764         else if (context instanceof BufferedCodeBuilder) {
 765             // Hijack the label
 766             return lab.getBCI();
 767         }
 768         else {
 769             throw new IllegalStateException(String.format("Unexpected label context %s in =%s", context, this));
 770         }
 771     }
 772 
 773     public void setLineNumber(int lineNo) {
 774         if (lineNumberWriter == null)
 775             lineNumberWriter = new DedupLineNumberTableAttribute(constantPool, context);
 776         lineNumberWriter.writeLineNumber(curPc(), lineNo);
 777     }
 778 
 779     public void setLabelTarget(Label label) {
 780         setLabelTarget(label, curPc());
 781     }
 782 
 783     @Override
 784     public void setLabelTarget(Label label, int bci) {
 785         LabelImpl lab = (LabelImpl) label;
 786         if (lab.labelContext() == this) {
 787             if (lab.getBCI() != -1)
 788                 throw new IllegalArgumentException("Setting label target for already-set label");
 789             lab.setBCI(bci);
 790         } else {
 791             setLabelTarget(lab, bci);
 792         }
 793     }
 794 
 795     private void setLabelTarget(LabelImpl lab, int bci) {
 796         LabelContext context = lab.labelContext();
 797         if (context == mruParent) {
 798             mruParentTable[lab.getBCI()] = bci + 1;
 799         }
 800         else if (context instanceof CodeAttribute parent) {
 801             if (parentMap == null)
 802                 parentMap = new IdentityHashMap<>();
 803             int[] table = parentMap.computeIfAbsent(parent, new Function<CodeAttribute, int[]>() {
 804                 @Override
 805                 public int[] apply(CodeAttribute x) {
 806                     return new int[parent.codeLength() + 1];
 807                 }
 808             });
 809 
 810             mruParent = parent;
 811             mruParentTable = table;
 812             table[lab.getBCI()] = bci + 1;
 813         }
 814         else if (context instanceof BufferedCodeBuilder) {
 815             // Hijack the label
 816             lab.setBCI(bci);
 817         }
 818         else {
 819             throw new IllegalStateException(String.format("Unexpected label context %s in =%s", context, this));
 820         }
 821     }
 822 
 823     public void addCharacterRange(CharacterRange element) {
 824         if (characterRangesCount >= characterRanges.length) {
 825             int newCapacity = characterRangesCount + 8;
 826             this.characterRanges = Arrays.copyOf(characterRanges, newCapacity);
 827         }
 828         characterRanges[characterRangesCount++] = element;
 829     }
 830 
 831     public void addLabel(DeferredLabel label) {
 832         if (deferredLabelsCount >= deferredLabels.length) {
 833             int newCapacity = deferredLabelsCount + 8;
 834             this.deferredLabels = Arrays.copyOf(deferredLabels, newCapacity);
 835         }
 836         deferredLabels[deferredLabelsCount++] = label;
 837     }
 838 
 839     public void addHandler(ExceptionCatch element) {
 840         AbstractPseudoInstruction.ExceptionCatchImpl el = (AbstractPseudoInstruction.ExceptionCatchImpl) element;
 841         ClassEntry type = el.catchTypeEntry();
 842         if (type != null && !constantPool.canWriteDirect(type.constantPool()))
 843             el = new AbstractPseudoInstruction.ExceptionCatchImpl(element.handler(), element.tryStart(), element.tryEnd(), AbstractPoolEntry.maybeClone(constantPool, type));
 844         handlers.add(el);
 845     }
 846 
 847     public void addLocalVariable(LocalVariable element) {
 848         if (localVariablesCount >= localVariables.length) {
 849             int newCapacity = localVariablesCount + 8;
 850             this.localVariables = Arrays.copyOf(localVariables, newCapacity);
 851         }
 852         localVariables[localVariablesCount++] = element;
 853     }
 854 
 855     public void addLocalVariableType(LocalVariableType element) {
 856         if (localVariableTypesCount >= localVariableTypes.length) {
 857             int newCapacity = localVariableTypesCount + 8;
 858             this.localVariableTypes = Arrays.copyOf(localVariableTypes, newCapacity);
 859         }
 860         localVariableTypes[localVariableTypesCount++] = element;
 861     }
 862 
 863     @Override
 864     public String toString() {
 865         return String.format("CodeBuilder[id=%d]", System.identityHashCode(this));
 866     }
 867 
 868     //ToDo: consolidate and open all exceptions
 869     private static final class LabelOverflowException extends IllegalArgumentException {
 870 
 871         private static final long serialVersionUID = 1L;
 872 
 873         public LabelOverflowException() {
 874             super("Label target offset overflow");
 875         }
 876     }
 877 
 878     // Fast overrides to avoid intermediate instructions
 879     // These are helpful for direct class building
 880 
 881     @Override
 882     public CodeBuilder return_() {
 883         bytecodesBufWriter.writeU1(RETURN);
 884         return this;
 885     }
 886 
 887     @Override
 888     public CodeBuilder return_(TypeKind tk) {
 889         bytecodesBufWriter.writeU1(returnBytecode(tk));
 890         return this;
 891     }
 892 
 893     @Override
 894     public CodeBuilder storeLocal(TypeKind tk, int slot) {
 895         return switch (tk) {
 896             case INT, SHORT, BYTE, CHAR, BOOLEAN
 897                            -> istore(slot);
 898             case LONG      -> lstore(slot);
 899             case DOUBLE    -> dstore(slot);
 900             case FLOAT     -> fstore(slot);
 901             case REFERENCE -> astore(slot);
 902             case VOID      -> throw new IllegalArgumentException("void");
 903         };
 904     }
 905 
 906     @Override
 907     public CodeBuilder labelBinding(Label label) {
 908         setLabelTarget(label, curPc());
 909         return this;
 910     }
 911 
 912     @Override
 913     public CodeBuilder loadLocal(TypeKind tk, int slot) {
 914         return switch (tk) {
 915             case INT, SHORT, BYTE, CHAR, BOOLEAN
 916                            -> iload(slot);
 917             case LONG      -> lload(slot);
 918             case DOUBLE    -> dload(slot);
 919             case FLOAT     -> fload(slot);
 920             case REFERENCE -> aload(slot);
 921             case VOID      -> throw new IllegalArgumentException("void");
 922         };
 923     }
 924 
 925     @Override
 926     public CodeBuilder invoke(Opcode opcode, MemberRefEntry ref) {
 927         if (opcode == Opcode.INVOKEINTERFACE) {
 928             int slots = Util.parameterSlots(Util.methodTypeSymbol(ref.type())) + 1;
 929             writeInvokeInterface(opcode, (InterfaceMethodRefEntry) ref, slots);
 930         } else {
 931             writeInvokeNormal(opcode, ref);
 932         }
 933         return this;
 934     }
 935 
 936     @Override
 937     public CodeBuilder invokespecial(ClassDesc owner, String name, MethodTypeDesc type) {
 938         bytecodesBufWriter.writeIndex(INVOKESPECIAL, constantPool().methodRefEntry(owner, name, type));
 939         return this;
 940     }
 941 
 942     @Override
 943     public CodeBuilder invokestatic(ClassDesc owner, String name, MethodTypeDesc type) {
 944         bytecodesBufWriter.writeIndex(INVOKESTATIC, constantPool().methodRefEntry(owner, name, type));
 945         return this;
 946     }
 947 
 948     @Override
 949     public CodeBuilder invokevirtual(ClassDesc owner, String name, MethodTypeDesc type) {
 950         bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, constantPool().methodRefEntry(owner, name, type));
 951         return this;
 952     }
 953 
 954     @Override
 955     public CodeBuilder getfield(ClassDesc owner, String name, ClassDesc type) {
 956         bytecodesBufWriter.writeIndex(GETFIELD, constantPool().fieldRefEntry(owner, name, type));
 957         return this;
 958     }
 959 
 960     @Override
 961     public CodeBuilder fieldAccess(Opcode opcode, FieldRefEntry ref) {
 962         bytecodesBufWriter.writeIndex(opcode.bytecode(), ref);
 963         return this;
 964     }
 965 
 966     @Override
 967     public CodeBuilder arrayLoad(TypeKind tk) {
 968         bytecodesBufWriter.writeU1(BytecodeHelpers.arrayLoadBytecode(tk));
 969         return this;
 970     }
 971 
 972     @Override
 973     public CodeBuilder arrayStore(TypeKind tk) {
 974         bytecodesBufWriter.writeU1(BytecodeHelpers.arrayStoreBytecode(tk));
 975         return this;
 976     }
 977 
 978     @Override
 979     public CodeBuilder branch(Opcode op, Label target) {
 980         writeBranch(op, target);
 981         return this;
 982     }
 983 
 984     @Override
 985     public CodeBuilder nop() {
 986         bytecodesBufWriter.writeU1(NOP);
 987         return this;
 988     }
 989 
 990     @Override
 991     public CodeBuilder aconst_null() {
 992         bytecodesBufWriter.writeU1(ACONST_NULL);
 993         return this;
 994     }
 995 
 996     @Override
 997     public CodeBuilder aload(int slot) {
 998         if (slot >= 0 && slot <= 3) {
 999             bytecodesBufWriter.writeU1(ALOAD_0 + slot);
1000         } else {
1001             localAccess(ALOAD, slot);
1002         }
1003         return this;
1004     }
1005 
1006     @Override
1007     public CodeBuilder anewarray(ClassEntry entry) {
1008         writeNewReferenceArray(entry);
1009         return this;
1010     }
1011 
1012     @Override
1013     public CodeBuilder arraylength() {
1014         bytecodesBufWriter.writeU1(ARRAYLENGTH);
1015         return this;
1016     }
1017 
1018     @Override
1019     public CodeBuilder areturn() {
1020         bytecodesBufWriter.writeU1(ARETURN);
1021         return this;
1022     }
1023 
1024     @Override
1025     public CodeBuilder astore(int slot) {
1026         if (slot >= 0 && slot <= 3) {
1027             bytecodesBufWriter.writeU1(ASTORE_0 + slot);
1028         } else {
1029             localAccess(ASTORE, slot);
1030         }
1031         return this;
1032     }
1033 
1034     @Override
1035     public CodeBuilder athrow() {
1036         bytecodesBufWriter.writeU1(ATHROW);
1037         return this;
1038     }
1039 
1040     @Override
1041     public CodeBuilder bipush(int b) {
1042         BytecodeHelpers.validateBipush(b);
1043         bytecodesBufWriter.writeU1U1(BIPUSH, b);
1044         return this;
1045     }
1046 
1047     @Override
1048     public CodeBuilder checkcast(ClassEntry type) {
1049         bytecodesBufWriter.writeIndex(CHECKCAST, type);
1050         return this;
1051     }
1052 
1053     @Override
1054     public CodeBuilder d2f() {
1055         bytecodesBufWriter.writeU1(D2F);
1056         return this;
1057     }
1058 
1059     @Override
1060     public CodeBuilder d2i() {
1061         bytecodesBufWriter.writeU1(D2I);
1062         return this;
1063     }
1064 
1065     @Override
1066     public CodeBuilder d2l() {
1067         bytecodesBufWriter.writeU1(D2L);
1068         return this;
1069     }
1070 
1071     @Override
1072     public CodeBuilder dadd() {
1073         bytecodesBufWriter.writeU1(DADD);
1074         return this;
1075     }
1076 
1077     @Override
1078     public CodeBuilder dcmpg() {
1079         bytecodesBufWriter.writeU1(DCMPG);
1080         return this;
1081     }
1082 
1083     @Override
1084     public CodeBuilder dcmpl() {
1085         bytecodesBufWriter.writeU1(DCMPL);
1086         return this;
1087     }
1088 
1089     @Override
1090     public CodeBuilder dconst_0() {
1091         bytecodesBufWriter.writeU1(DCONST_0);
1092         return this;
1093     }
1094 
1095     @Override
1096     public CodeBuilder dconst_1() {
1097         bytecodesBufWriter.writeU1(DCONST_1);
1098         return this;
1099     }
1100 
1101     @Override
1102     public CodeBuilder ddiv() {
1103         bytecodesBufWriter.writeU1(DDIV);
1104         return this;
1105     }
1106 
1107     @Override
1108     public CodeBuilder dload(int slot) {
1109         if (slot >= 0 && slot <= 3) {
1110             bytecodesBufWriter.writeU1(DLOAD_0 + slot);
1111         } else {
1112             localAccess(DLOAD, slot);
1113         }
1114         return this;
1115     }
1116 
1117     @Override
1118     public CodeBuilder dmul() {
1119         bytecodesBufWriter.writeU1(DMUL);
1120         return this;
1121     }
1122 
1123     @Override
1124     public CodeBuilder dneg() {
1125         bytecodesBufWriter.writeU1(DNEG);
1126         return this;
1127     }
1128 
1129     @Override
1130     public CodeBuilder drem() {
1131         bytecodesBufWriter.writeU1(DREM);
1132         return this;
1133     }
1134 
1135     @Override
1136     public CodeBuilder dreturn() {
1137         bytecodesBufWriter.writeU1(DRETURN);
1138         return this;
1139     }
1140 
1141     @Override
1142     public CodeBuilder dstore(int slot) {
1143         if (slot >= 0 && slot <= 3) {
1144             bytecodesBufWriter.writeU1(DSTORE_0 + slot);
1145         } else {
1146             localAccess(DSTORE, slot);
1147         }
1148         return this;
1149     }
1150 
1151     @Override
1152     public CodeBuilder dsub() {
1153         bytecodesBufWriter.writeU1(DSUB);
1154         return this;
1155     }
1156 
1157     @Override
1158     public CodeBuilder dup() {
1159         bytecodesBufWriter.writeU1(DUP);
1160         return this;
1161     }
1162 
1163     @Override
1164     public CodeBuilder dup2() {
1165         bytecodesBufWriter.writeU1(DUP2);
1166         return this;
1167     }
1168 
1169     @Override
1170     public CodeBuilder dup2_x1() {
1171         bytecodesBufWriter.writeU1(DUP2_X1);
1172         return this;
1173     }
1174 
1175     @Override
1176     public CodeBuilder dup2_x2() {
1177         bytecodesBufWriter.writeU1(DUP2_X2);
1178         return this;
1179     }
1180 
1181     @Override
1182     public CodeBuilder dup_x1() {
1183         bytecodesBufWriter.writeU1(DUP_X1);
1184         return this;
1185     }
1186 
1187     @Override
1188     public CodeBuilder dup_x2() {
1189         bytecodesBufWriter.writeU1(DUP_X2);
1190         return this;
1191     }
1192 
1193     @Override
1194     public CodeBuilder f2d() {
1195         bytecodesBufWriter.writeU1(F2D);
1196         return this;
1197     }
1198 
1199     @Override
1200     public CodeBuilder f2i() {
1201         bytecodesBufWriter.writeU1(F2I);
1202         return this;
1203     }
1204 
1205     @Override
1206     public CodeBuilder f2l() {
1207         bytecodesBufWriter.writeU1(F2L);
1208         return this;
1209     }
1210 
1211     @Override
1212     public CodeBuilder fadd() {
1213         bytecodesBufWriter.writeU1(FADD);
1214         return this;
1215     }
1216 
1217     @Override
1218     public CodeBuilder fcmpg() {
1219         bytecodesBufWriter.writeU1(FCMPG);
1220         return this;
1221     }
1222 
1223     @Override
1224     public CodeBuilder fcmpl() {
1225         bytecodesBufWriter.writeU1(FCMPL);
1226         return this;
1227     }
1228 
1229     @Override
1230     public CodeBuilder fconst_0() {
1231         bytecodesBufWriter.writeU1(FCONST_0);
1232         return this;
1233     }
1234 
1235     @Override
1236     public CodeBuilder fconst_1() {
1237         bytecodesBufWriter.writeU1(FCONST_1);
1238         return this;
1239     }
1240 
1241     @Override
1242     public CodeBuilder fconst_2() {
1243         bytecodesBufWriter.writeU1(FCONST_2);
1244         return this;
1245     }
1246 
1247     @Override
1248     public CodeBuilder fdiv() {
1249         bytecodesBufWriter.writeU1(FDIV);
1250         return this;
1251     }
1252 
1253     @Override
1254     public CodeBuilder fload(int slot) {
1255         if (slot >= 0 && slot <= 3) {
1256             bytecodesBufWriter.writeU1(FLOAD_0 + slot);
1257         } else {
1258             localAccess(FLOAD, slot);
1259         }
1260         return this;
1261     }
1262 
1263     @Override
1264     public CodeBuilder fmul() {
1265         bytecodesBufWriter.writeU1(FMUL);
1266         return this;
1267     }
1268 
1269     @Override
1270     public CodeBuilder fneg() {
1271         bytecodesBufWriter.writeU1(FNEG);
1272         return this;
1273     }
1274 
1275     @Override
1276     public CodeBuilder frem() {
1277         bytecodesBufWriter.writeU1(FREM);
1278         return this;
1279     }
1280 
1281     @Override
1282     public CodeBuilder freturn() {
1283         bytecodesBufWriter.writeU1(FRETURN);
1284         return this;
1285     }
1286 
1287     @Override
1288     public CodeBuilder fstore(int slot) {
1289         if (slot >= 0 && slot <= 3) {
1290             bytecodesBufWriter.writeU1(FSTORE_0 + slot);
1291         } else {
1292             localAccess(FSTORE, slot);
1293         }
1294         return this;
1295     }
1296 
1297     @Override
1298     public CodeBuilder fsub() {
1299         bytecodesBufWriter.writeU1(FSUB);
1300         return this;
1301     }
1302 
1303     @Override
1304     public CodeBuilder getstatic(ClassDesc owner, String name, ClassDesc type) {
1305         bytecodesBufWriter.writeIndex(GETSTATIC, constantPool().fieldRefEntry(owner, name, type));
1306         return this;
1307     }
1308 
1309     @Override
1310     public CodeBuilder goto_(Label target) {
1311         writeShortJump(GOTO, target);
1312         return this;
1313     }
1314 
1315     @Override
1316     public CodeBuilder i2b() {
1317         bytecodesBufWriter.writeU1(I2B);
1318         return this;
1319     }
1320 
1321     @Override
1322     public CodeBuilder i2c() {
1323         bytecodesBufWriter.writeU1(I2C);
1324         return this;
1325     }
1326 
1327     @Override
1328     public CodeBuilder i2d() {
1329         bytecodesBufWriter.writeU1(I2D);
1330         return this;
1331     }
1332 
1333     @Override
1334     public CodeBuilder i2f() {
1335         bytecodesBufWriter.writeU1(I2F);
1336         return this;
1337     }
1338 
1339     @Override
1340     public CodeBuilder i2l() {
1341         bytecodesBufWriter.writeU1(I2L);
1342         return this;
1343     }
1344 
1345     @Override
1346     public CodeBuilder i2s() {
1347         bytecodesBufWriter.writeU1(I2S);
1348         return this;
1349     }
1350 
1351     @Override
1352     public CodeBuilder iadd() {
1353         bytecodesBufWriter.writeU1(IADD);
1354         return this;
1355     }
1356 
1357     @Override
1358     public CodeBuilder iand() {
1359         bytecodesBufWriter.writeU1(IAND);
1360         return this;
1361     }
1362 
1363     @Override
1364     public CodeBuilder iconst_0() {
1365         bytecodesBufWriter.writeU1(ICONST_0);
1366         return this;
1367     }
1368 
1369     @Override
1370     public CodeBuilder iconst_1() {
1371         bytecodesBufWriter.writeU1(ICONST_1);
1372         return this;
1373     }
1374 
1375     @Override
1376     public CodeBuilder iconst_2() {
1377         bytecodesBufWriter.writeU1(ICONST_2);
1378         return this;
1379     }
1380 
1381     @Override
1382     public CodeBuilder iconst_3() {
1383         bytecodesBufWriter.writeU1(ICONST_3);
1384         return this;
1385     }
1386 
1387     @Override
1388     public CodeBuilder iconst_4() {
1389         bytecodesBufWriter.writeU1(ICONST_4);
1390         return this;
1391     }
1392 
1393     @Override
1394     public CodeBuilder iconst_5() {
1395         bytecodesBufWriter.writeU1(ICONST_5);
1396         return this;
1397     }
1398 
1399     @Override
1400     public CodeBuilder iconst_m1() {
1401         bytecodesBufWriter.writeU1(ICONST_M1);
1402         return this;
1403     }
1404 
1405     @Override
1406     public CodeBuilder idiv() {
1407         bytecodesBufWriter.writeU1(IDIV);
1408         return this;
1409     }
1410 
1411     @Override
1412     public CodeBuilder if_acmpeq(Label target) {
1413         writeShortJump(IF_ACMPEQ, target);
1414         return this;
1415     }
1416 
1417     @Override
1418     public CodeBuilder if_acmpne(Label target) {
1419         writeShortJump(IF_ACMPNE, target);
1420         return this;
1421     }
1422 
1423     @Override
1424     public CodeBuilder if_icmpeq(Label target) {
1425         writeShortJump(IF_ICMPEQ, target);
1426         return this;
1427     }
1428 
1429     @Override
1430     public CodeBuilder if_icmpge(Label target) {
1431         writeShortJump(IF_ICMPGE, target);
1432         return this;
1433     }
1434 
1435     @Override
1436     public CodeBuilder if_icmpgt(Label target) {
1437         writeShortJump(IF_ICMPGT, target);
1438         return this;
1439     }
1440 
1441     @Override
1442     public CodeBuilder if_icmple(Label target) {
1443         writeShortJump(IF_ICMPLE, target);
1444         return this;
1445     }
1446 
1447     @Override
1448     public CodeBuilder if_icmplt(Label target) {
1449         writeShortJump(IF_ICMPLT, target);
1450         return this;
1451     }
1452 
1453     @Override
1454     public CodeBuilder if_icmpne(Label target) {
1455         writeShortJump(IF_ICMPNE, target);
1456         return this;
1457     }
1458 
1459     @Override
1460     public CodeBuilder ifnonnull(Label target) {
1461         writeShortJump(IFNONNULL, target);
1462         return this;
1463     }
1464 
1465     @Override
1466     public CodeBuilder ifnull(Label target) {
1467         writeShortJump(IFNULL, target);
1468         return this;
1469     }
1470 
1471     @Override
1472     public CodeBuilder ifeq(Label target) {
1473         writeShortJump(IFEQ, target);
1474         return this;
1475     }
1476 
1477     @Override
1478     public CodeBuilder ifge(Label target) {
1479         writeShortJump(IFGE, target);
1480         return this;
1481     }
1482 
1483     @Override
1484     public CodeBuilder ifgt(Label target) {
1485         writeShortJump(IFGT, target);
1486         return this;
1487     }
1488 
1489     @Override
1490     public CodeBuilder ifle(Label target) {
1491         writeShortJump(IFLE, target);
1492         return this;
1493     }
1494 
1495     @Override
1496     public CodeBuilder iflt(Label target) {
1497         writeShortJump(IFLT, target);
1498         return this;
1499     }
1500 
1501     @Override
1502     public CodeBuilder ifne(Label target) {
1503         writeShortJump(IFNE, target);
1504         return this;
1505     }
1506 
1507     @Override
1508     public CodeBuilder iinc(int slot, int val) {
1509         writeIncrement(validateAndIsWideIinc(slot, val), slot, val);
1510         return this;
1511     }
1512 
1513     @Override
1514     public CodeBuilder iload(int slot) {
1515         if (slot >= 0 && slot <= 3) {
1516             bytecodesBufWriter.writeU1(ILOAD_0 + slot);
1517         } else {
1518             localAccess(ILOAD, slot);
1519         }
1520         return this;
1521     }
1522 
1523     @Override
1524     public CodeBuilder imul() {
1525         bytecodesBufWriter.writeU1(IMUL);
1526         return this;
1527     }
1528 
1529     @Override
1530     public CodeBuilder ineg() {
1531         bytecodesBufWriter.writeU1(INEG);
1532         return this;
1533     }
1534 
1535     @Override
1536     public CodeBuilder instanceOf(ClassEntry target) {
1537         bytecodesBufWriter.writeIndex(INSTANCEOF, target);
1538         return this;
1539     }
1540 
1541     @Override
1542     public CodeBuilder invokedynamic(InvokeDynamicEntry ref) {
1543         writeInvokeDynamic(ref);
1544         return this;
1545     }
1546 
1547     @Override
1548     public CodeBuilder invokeinterface(InterfaceMethodRefEntry ref) {
1549         writeInvokeInterface(Opcode.INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1);
1550         return this;
1551     }
1552 
1553     @Override
1554     public CodeBuilder invokespecial(InterfaceMethodRefEntry ref) {
1555         bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref);
1556         return this;
1557     }
1558 
1559     @Override
1560     public CodeBuilder invokespecial(MethodRefEntry ref) {
1561         bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref);
1562         return this;
1563     }
1564 
1565     @Override
1566     public CodeBuilder invokestatic(InterfaceMethodRefEntry ref) {
1567         bytecodesBufWriter.writeIndex(INVOKESTATIC, ref);
1568         return this;
1569     }
1570 
1571     @Override
1572     public CodeBuilder invokestatic(MethodRefEntry ref) {
1573         bytecodesBufWriter.writeIndex(INVOKESTATIC, ref);
1574         return this;
1575     }
1576 
1577     @Override
1578     public CodeBuilder invokevirtual(MethodRefEntry ref) {
1579         bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, ref);
1580         return this;
1581     }
1582 
1583     @Override
1584     public CodeBuilder ior() {
1585         bytecodesBufWriter.writeU1(IOR);
1586         return this;
1587     }
1588 
1589     @Override
1590     public CodeBuilder irem() {
1591         bytecodesBufWriter.writeU1(IREM);
1592         return this;
1593     }
1594 
1595     @Override
1596     public CodeBuilder ireturn() {
1597         bytecodesBufWriter.writeU1(IRETURN);
1598         return this;
1599     }
1600 
1601     @Override
1602     public CodeBuilder ishl() {
1603         bytecodesBufWriter.writeU1(ISHL);
1604         return this;
1605     }
1606 
1607     @Override
1608     public CodeBuilder ishr() {
1609         bytecodesBufWriter.writeU1(ISHR);
1610         return this;
1611     }
1612 
1613     @Override
1614     public CodeBuilder istore(int slot) {
1615         if (slot >= 0 && slot <= 3) {
1616             bytecodesBufWriter.writeU1(ISTORE_0 + slot);
1617         } else {
1618             localAccess(ISTORE, slot);
1619         }
1620         return this;
1621     }
1622 
1623     @Override
1624     public CodeBuilder isub() {
1625         bytecodesBufWriter.writeU1(ISUB);
1626         return this;
1627     }
1628 
1629     @Override
1630     public CodeBuilder iushr() {
1631         bytecodesBufWriter.writeU1(IUSHR);
1632         return this;
1633     }
1634 
1635     @Override
1636     public CodeBuilder ixor() {
1637         bytecodesBufWriter.writeU1(IXOR);
1638         return this;
1639     }
1640 
1641     @Override
1642     public CodeBuilder lookupswitch(Label defaultTarget, List<SwitchCase> cases) {
1643         writeLookupSwitch(defaultTarget, cases);
1644         return this;
1645     }
1646 
1647     @Override
1648     public CodeBuilder l2d() {
1649         bytecodesBufWriter.writeU1(L2D);
1650         return this;
1651     }
1652 
1653     @Override
1654     public CodeBuilder l2f() {
1655         bytecodesBufWriter.writeU1(L2F);
1656         return this;
1657     }
1658 
1659     @Override
1660     public CodeBuilder l2i() {
1661         bytecodesBufWriter.writeU1(L2I);
1662         return this;
1663     }
1664 
1665     @Override
1666     public CodeBuilder ladd() {
1667         bytecodesBufWriter.writeU1(LADD);
1668         return this;
1669     }
1670 
1671     @Override
1672     public CodeBuilder land() {
1673         bytecodesBufWriter.writeU1(LAND);
1674         return this;
1675     }
1676 
1677     @Override
1678     public CodeBuilder lcmp() {
1679         bytecodesBufWriter.writeU1(LCMP);
1680         return this;
1681     }
1682 
1683     @Override
1684     public CodeBuilder lconst_0() {
1685         bytecodesBufWriter.writeU1(LCONST_0);
1686         return this;
1687     }
1688 
1689     @Override
1690     public CodeBuilder lconst_1() {
1691         bytecodesBufWriter.writeU1(LCONST_1);
1692         return this;
1693     }
1694 
1695     @Override
1696     public CodeBuilder ldc(LoadableConstantEntry entry) {
1697         var direct = AbstractPoolEntry.maybeClone(constantPool, entry);
1698         writeDirectLoadConstant(BytecodeHelpers.ldcOpcode(direct), direct);
1699         return this;
1700     }
1701 
1702     @Override
1703     public CodeBuilder ldiv() {
1704         bytecodesBufWriter.writeU1(LDIV);
1705         return this;
1706     }
1707 
1708     @Override
1709     public CodeBuilder lload(int slot) {
1710         if (slot >= 0 && slot <= 3) {
1711             bytecodesBufWriter.writeU1(LLOAD_0 + slot);
1712         } else {
1713             localAccess(LLOAD, slot);
1714         }
1715         return this;
1716     }
1717 
1718     @Override
1719     public CodeBuilder lmul() {
1720         bytecodesBufWriter.writeU1(LMUL);
1721         return this;
1722     }
1723 
1724     @Override
1725     public CodeBuilder lneg() {
1726         bytecodesBufWriter.writeU1(LNEG);
1727         return this;
1728     }
1729 
1730     @Override
1731     public CodeBuilder lor() {
1732         bytecodesBufWriter.writeU1(LOR);
1733         return this;
1734     }
1735 
1736     @Override
1737     public CodeBuilder lrem() {
1738         bytecodesBufWriter.writeU1(LREM);
1739         return this;
1740     }
1741 
1742     @Override
1743     public CodeBuilder lreturn() {
1744         bytecodesBufWriter.writeU1(LRETURN);
1745         return this;
1746     }
1747 
1748     @Override
1749     public CodeBuilder lshl() {
1750         bytecodesBufWriter.writeU1(LSHL);
1751         return this;
1752     }
1753 
1754     @Override
1755     public CodeBuilder lshr() {
1756         bytecodesBufWriter.writeU1(LSHR);
1757         return this;
1758     }
1759 
1760     @Override
1761     public CodeBuilder lstore(int slot) {
1762         if (slot >= 0 && slot <= 3) {
1763             bytecodesBufWriter.writeU1(LSTORE_0 + slot);
1764         } else {
1765             localAccess(LSTORE, slot);
1766         }
1767         return this;
1768     }
1769 
1770     @Override
1771     public CodeBuilder lsub() {
1772         bytecodesBufWriter.writeU1(LSUB);
1773         return this;
1774     }
1775 
1776     @Override
1777     public CodeBuilder lushr() {
1778         bytecodesBufWriter.writeU1(LUSHR);
1779         return this;
1780     }
1781 
1782     @Override
1783     public CodeBuilder lxor() {
1784         bytecodesBufWriter.writeU1(LXOR);
1785         return this;
1786     }
1787 
1788     @Override
1789     public CodeBuilder monitorenter() {
1790         bytecodesBufWriter.writeU1(MONITORENTER);
1791         return this;
1792     }
1793 
1794     @Override
1795     public CodeBuilder monitorexit() {
1796         bytecodesBufWriter.writeU1(MONITOREXIT);
1797         return this;
1798     }
1799 
1800     @Override
1801     public CodeBuilder multianewarray(ClassEntry array, int dims) {
1802         writeNewMultidimensionalArray(dims, array);
1803         return this;
1804     }
1805 
1806     @Override
1807     public CodeBuilder new_(ClassEntry clazz) {
1808         writeNewObject(clazz);
1809         return this;
1810     }
1811 
1812     @Override
1813     public CodeBuilder newarray(TypeKind typeKind) {
1814         int atype = typeKind.newarrayCode(); // implicit null check
1815         if (atype < 0)
1816             throw new IllegalArgumentException("Illegal component type: ".concat(typeKind.upperBound().displayName()));
1817         writeNewPrimitiveArray(atype);
1818         return this;
1819     }
1820 
1821     @Override
1822     public CodeBuilder pop() {
1823         bytecodesBufWriter.writeU1(POP);
1824         return this;
1825     }
1826 
1827     @Override
1828     public CodeBuilder pop2() {
1829         bytecodesBufWriter.writeU1(POP2);
1830         return this;
1831     }
1832 
1833     @Override
1834     public CodeBuilder sipush(int s) {
1835         BytecodeHelpers.validateSipush(s);
1836         bytecodesBufWriter.writeU1U2(SIPUSH, s);
1837         return this;
1838     }
1839 
1840     @Override
1841     public CodeBuilder swap() {
1842         bytecodesBufWriter.writeU1(SWAP);
1843         return this;
1844     }
1845 
1846     @Override
1847     public CodeBuilder tableswitch(int low, int high, Label defaultTarget, List<SwitchCase> cases) {
1848         writeTableSwitch(low, high, defaultTarget, cases);
1849         return this;
1850     }
1851 }