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