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