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