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