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