All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.dmlloyd.classfile.impl.DirectCodeBuilder Maven / Gradle / Ivy

There is a newer version: 24.cr2
Show newest version
/*
 * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package io.github.dmlloyd.classfile.impl;

import io.github.dmlloyd.classfile.*;
import io.github.dmlloyd.classfile.attribute.CodeAttribute;
import io.github.dmlloyd.classfile.attribute.LineNumberTableAttribute;
import io.github.dmlloyd.classfile.constantpool.*;
import io.github.dmlloyd.classfile.instruction.CharacterRange;
import io.github.dmlloyd.classfile.instruction.ExceptionCatch;
import io.github.dmlloyd.classfile.instruction.LocalVariable;
import io.github.dmlloyd.classfile.instruction.LocalVariableType;
import io.github.dmlloyd.classfile.instruction.SwitchCase;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;
import static io.github.dmlloyd.classfile.impl.BytecodeHelpers.*;
import static io.github.dmlloyd.classfile.impl.RawBytecodeHelper.*;

public final class DirectCodeBuilder
        extends AbstractDirectBuilder
        implements TerminalCodeBuilder {
    private static final CharacterRange[] EMPTY_CHARACTER_RANGE = {};
    private static final DeferredLabel[] EMPTY_LABEL_ARRAY = {};
    private static final LocalVariable[] EMPTY_LOCAL_VARIABLE_ARRAY = {};
    private static final LocalVariableType[] EMPTY_LOCAL_VARIABLE_TYPE_ARRAY = {};
    private static final AbstractPseudoInstruction.ExceptionCatchImpl[] EMPTY_HANDLER_ARRAY = {};
    private static final DeferredLabel[] EMPTY_DEFERRED_LABEL_ARRAY = {};

    final List handlers = new ArrayList<>();
    private CharacterRange[] characterRanges = EMPTY_CHARACTER_RANGE;
    private LocalVariable[] localVariables = EMPTY_LOCAL_VARIABLE_ARRAY;
    private LocalVariableType[] localVariableTypes = EMPTY_LOCAL_VARIABLE_TYPE_ARRAY;
    private int characterRangesCount = 0;
    private int localVariablesCount = 0;
    private int localVariableTypesCount = 0;
    private final boolean transformDeferredJumps, transformKnownJumps;
    private final Label startLabel, endLabel;
    final MethodInfo methodInfo;
    final BufWriterImpl bytecodesBufWriter;
    private CodeAttribute mruParent;
    private int[] mruParentTable;
    private Map parentMap;
    private DedupLineNumberTableAttribute lineNumberWriter;
    private int topLocal;

    private DeferredLabel[] deferredLabels = EMPTY_DEFERRED_LABEL_ARRAY;
    private int deferredLabelsCount = 0;

    /* Locals management
       lazily computed maxLocal = -1
       first time: derive count from methodType descriptor (for new methods) & ACC_STATIC,
       or model maxLocals (for transformation)
       block builders inherit parent count
       allocLocal(TypeKind) bumps by nSlots
     */

    public static UnboundAttribute build(MethodInfo methodInfo,
                                                 Consumer handler,
                                                 SplitConstantPool constantPool,
                                                 ClassFileImpl context,
                                                 CodeModel original) {
        DirectCodeBuilder cb;
        try {
            handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, false));
            cb.buildContent();
        } catch (LabelOverflowException loe) {
            if (context.fixShortJumps()) {
                handler.accept(cb = new DirectCodeBuilder(methodInfo, constantPool, context, original, true));
                cb.buildContent();
            }
            else
                throw loe;
        }
        return cb.content;
    }

    private DirectCodeBuilder(MethodInfo methodInfo,
                              SplitConstantPool constantPool,
                              ClassFileImpl context,
                              CodeModel original,
                              boolean transformDeferredJumps) {
        super(constantPool, context);
        setOriginal(original);
        this.methodInfo = methodInfo;
        this.transformDeferredJumps = transformDeferredJumps;
        this.transformKnownJumps = context.fixShortJumps();
        bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength())
                : new BufWriterImpl(constantPool, context);
        this.startLabel = new LabelImpl(this, 0);
        this.endLabel = new LabelImpl(this, -1);
        this.topLocal = TerminalCodeBuilder.setupTopLocal(methodInfo, original);
    }

    @Override
    public CodeBuilder with(CodeElement element) {
        if (element instanceof AbstractElement ae) {
            ae.writeTo(this);
        } else {
            writeAttribute((CustomAttribute) requireNonNull(element));
        }
        return this;
    }

    @Override
    public Label newLabel() {
        return new LabelImpl(this, -1);
    }

    @Override
    public Label startLabel() {
        return startLabel;
    }

    @Override
    public Label endLabel() {
        return endLabel;
    }

    @Override
    public int receiverSlot() {
        return methodInfo.receiverSlot();
    }

    @Override
    public int parameterSlot(int paramNo) {
        return methodInfo.parameterSlot(paramNo);
    }

    public int curTopLocal() {
        return topLocal;
    }

    @Override
    public int allocateLocal(TypeKind typeKind) {
        int retVal = topLocal;
        topLocal += typeKind.slotSize();
        return retVal;
    }

    public int curPc() {
        return bytecodesBufWriter.size();
    }

    public MethodInfo methodInfo() {
        return methodInfo;
    }

    private UnboundAttribute content = null;

    private void writeExceptionHandlers(BufWriterImpl buf) {
        int pos = buf.size();
        int handlersSize = handlers.size();
        buf.writeU2(handlersSize);
        if (handlersSize > 0) {
            writeExceptionHandlers(buf, pos);
        }
    }

    private void writeExceptionHandlers(BufWriterImpl buf, int pos) {
        int handlersSize = handlers.size();
        for (AbstractPseudoInstruction.ExceptionCatchImpl h : handlers) {
            int startPc = labelToBci(h.tryStart());
            int endPc = labelToBci(h.tryEnd());
            int handlerPc = labelToBci(h.handler());
            if (startPc == -1 || endPc == -1 || handlerPc == -1) {
                if (context.dropDeadLabels()) {
                    handlersSize--;
                } else {
                    throw new IllegalArgumentException("Unbound label in exception handler");
                }
            } else {
                buf.writeU2U2U2(startPc, endPc, handlerPc);
                buf.writeIndexOrZero(h.catchTypeEntry());
                handlersSize++;
            }
        }
        if (handlersSize < handlers.size())
            buf.patchU2(pos, handlersSize);
    }

    private void buildContent() {
        if (content != null) return;
        setLabelTarget(endLabel);

        // Backfill branches for which Label didn't have position yet
        processDeferredLabels();

        if (context.passDebugElements()) {
            if (characterRangesCount > 0) {
                Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) {

                    @Override
                    public void writeBody(BufWriterImpl b) {
                        int pos = b.size();
                        int crSize = characterRangesCount;
                        b.writeU2(crSize);
                        for (int i = 0; i < characterRangesCount; i++) {
                            CharacterRange cr = characterRanges[i];
                            var start = labelToBci(cr.startScope());
                            var end = labelToBci(cr.endScope());
                            if (start == -1 || end == -1) {
                                if (context.dropDeadLabels()) {
                                    crSize--;
                                } else {
                                    throw new IllegalArgumentException("Unbound label in character range");
                                }
                            } else {
                                b.writeU2U2(start, end - 1);
                                b.writeIntInt(cr.characterRangeStart(), cr.characterRangeEnd());
                                b.writeU2(cr.flags());
                            }
                        }
                        if (crSize < characterRangesCount)
                            b.patchU2(pos, crSize);
                    }

                    @Override
                    public Utf8Entry attributeName() {
                        return constantPool.utf8Entry(Attributes.NAME_CHARACTER_RANGE_TABLE);
                    }
                };
                attributes.withAttribute(a);
            }

            if (localVariablesCount > 0) {
                Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTable()) {
                    @Override
                    public void writeBody(BufWriterImpl b) {
                        int pos = b.size();
                        int lvSize = localVariablesCount;
                        b.writeU2(lvSize);
                        for (int i = 0; i < localVariablesCount; i++) {
                            LocalVariable l = localVariables[i];
                            if (!Util.writeLocalVariable(b, l)) {
                                if (context.dropDeadLabels()) {
                                    lvSize--;
                                } else {
                                    throw new IllegalArgumentException("Unbound label in local variable type");
                                }
                            }
                        }
                        if (lvSize < localVariablesCount)
                            b.patchU2(pos, lvSize);
                    }

                    @Override
                    public Utf8Entry attributeName() {
                        return constantPool.utf8Entry(Attributes.NAME_LOCAL_VARIABLE_TABLE);
                    }
                };
                attributes.withAttribute(a);
            }

            if (localVariableTypesCount > 0) {
                Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTypeTable()) {
                    @Override
                    public void writeBody(BufWriterImpl b) {
                        int pos = b.size();
                        int lvtSize = localVariableTypesCount;
                        b.writeU2(lvtSize);
                        for (int i = 0; i < localVariableTypesCount; i++) {
                            LocalVariableType l = localVariableTypes[i];
                            if (!Util.writeLocalVariable(b, l)) {
                                if (context.dropDeadLabels()) {
                                    lvtSize--;
                                } else {
                                    throw new IllegalArgumentException("Unbound label in local variable type");
                                }
                            }
                        }
                        if (lvtSize < localVariableTypesCount)
                            b.patchU2(pos, lvtSize);
                    }

                    @Override
                    public Utf8Entry attributeName() {
                        return constantPool.utf8Entry(Attributes.NAME_LOCAL_VARIABLE_TYPE_TABLE);
                    }
                };
                attributes.withAttribute(a);
            }
        }

        if (lineNumberWriter != null) {
            attributes.withAttribute(lineNumberWriter);
        }

        content = new UnboundAttribute.AdHocAttribute<>(Attributes.code()) {

            private void writeCounters(boolean codeMatch, BufWriterImpl buf) {
                if (codeMatch) {
                    var originalAttribute = (CodeImpl) original;
                    buf.writeU2U2(originalAttribute.maxStack(), originalAttribute.maxLocals());
                } else {
                    StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
                    buf.writeU2U2(cntr.maxStack(), cntr.maxLocals());
                }
            }

            private void generateStackMaps(BufWriterImpl buf) throws IllegalArgumentException {
                //new instance of generator immediately calculates maxStack, maxLocals, all frames,
                // patches dead bytecode blocks and removes them from exception table
                var dcb = DirectCodeBuilder.this;
                StackMapGenerator gen = StackMapGenerator.of(dcb, buf);
                dcb.attributes.withAttribute(gen.stackMapTableAttribute());
                buf.writeU2U2(gen.maxStack(), gen.maxLocals());
            }

            private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) {
                if (buf.getMajorVersion() >= ClassFile.JAVA_6_VERSION) {
                    try {
                        generateStackMaps(buf);
                    } catch (IllegalArgumentException e) {
                        //failover following JVMS-4.10
                        if (buf.getMajorVersion() == ClassFile.JAVA_6_VERSION) {
                            writeCounters(codeMatch, buf);
                        } else {
                            throw e;
                        }
                    }
                } else {
                    writeCounters(codeMatch, buf);
                }
            }

            @Override
            public void writeBody(BufWriterImpl buf) {
                DirectCodeBuilder dcb = DirectCodeBuilder.this;
                buf.setLabelContext(dcb);

                int codeLength = curPc();
                if (codeLength == 0 || codeLength >= 65536) {
                    throw new IllegalArgumentException(String.format(
                            "Code length %d is outside the allowed range in %s%s",
                            codeLength,
                            dcb.methodInfo.methodName().stringValue(),
                            dcb.methodInfo.methodTypeSymbol().displayDescriptor()));
                }

                boolean codeMatch = dcb.original != null && codeAndExceptionsMatch(codeLength);
                var context = dcb.context;
                if (context.stackMapsWhenRequired()) {
                    if (codeMatch) {
                        dcb.attributes.withAttribute(dcb.original.findAttribute(Attributes.stackMapTable()).orElse(null));
                        writeCounters(true, buf);
                    } else {
                        tryGenerateStackMaps(false, buf);
                    }
                } else if (context.generateStackMaps()) {
                    generateStackMaps(buf);
                } else if (context.dropStackMaps()) {
                    writeCounters(codeMatch, buf);
                }

                buf.writeInt(codeLength);
                buf.writeBytes(dcb.bytecodesBufWriter);
                dcb.writeExceptionHandlers(buf);
                dcb.attributes.writeTo(buf);
                buf.setLabelContext(null);
            }

            @Override
            public Utf8Entry attributeName() {
                return constantPool.utf8Entry(Attributes.NAME_CODE);
            }
        };
    }

    private static class DedupLineNumberTableAttribute extends UnboundAttribute.AdHocAttribute {
        private final BufWriterImpl buf;
        private int lastPc, lastLine, writtenLine;

        public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool, ClassFileImpl context) {
            super(Attributes.lineNumberTable());
            buf = new BufWriterImpl(constantPool, context);
            lastPc = -1;
            writtenLine = -1;
        }

        private void push() {
            //subsequent identical line numbers are skipped
            if (lastPc >= 0 && lastLine != writtenLine) {
                buf.writeU2U2(lastPc, lastLine);
                writtenLine = lastLine;
            }
        }

        //writes are expected ordered by pc in ascending sequence
        public void writeLineNumber(int pc, int lineNo) {
            //for each pc only the latest line number is written
            if (lastPc != pc && lastLine != lineNo) {
                push();
                lastPc = pc;
            }
            lastLine = lineNo;
        }

        @Override
        public void writeBody(BufWriterImpl b) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void writeTo(BufWriterImpl b) {
            b.writeIndex(b.constantPool().utf8Entry(Attributes.NAME_LINE_NUMBER_TABLE));
            push();
            b.writeInt(buf.size() + 2);
            b.writeU2(buf.size() / 4);
            b.writeBytes(buf);
        }

        @Override
        public Utf8Entry attributeName() {
            return buf.constantPool().utf8Entry(Attributes.NAME_LINE_NUMBER_TABLE);
        }
    }

    private boolean codeAndExceptionsMatch(int codeLength) {
        boolean codeAttributesMatch;
        if (original instanceof CodeImpl cai && canWriteDirect(cai.constantPool())) {
            codeAttributesMatch = cai.codeLength == curPc()
                                  && cai.compareCodeBytes(bytecodesBufWriter, 0, codeLength);
            if (codeAttributesMatch) {
                var bw = new BufWriterImpl(constantPool, context);
                writeExceptionHandlers(bw);
                codeAttributesMatch = cai.classReader.compare(bw, 0, cai.exceptionHandlerPos, bw.size());
            }
        }
        else
            codeAttributesMatch = false;
        return codeAttributesMatch;
    }

    // Writing support

    private record DeferredLabel(int labelPc, int size, int instructionPc, Label label) { }

    private void processDeferredLabels() {
        for (int i = 0; i < deferredLabelsCount; i++) {
            DeferredLabel dl = deferredLabels[i];
            int branchOffset = labelToBci(dl.label) - dl.instructionPc;
            if (dl.size == 2) {
                if ((short) branchOffset != branchOffset) throw new LabelOverflowException();
                bytecodesBufWriter.patchU2(dl.labelPc, branchOffset);
            } else {
                assert dl.size == 4;
                bytecodesBufWriter.patchInt(dl.labelPc, branchOffset);
            }
        }
    }

    // Instruction writing

    public void writeBytecode(Opcode opcode) {
        assert !opcode.isWide();
        bytecodesBufWriter.writeU1(opcode.bytecode());
    }

    // Instruction version, refer to opcode
    public void writeLocalVar(Opcode opcode, int slot) {
        if (opcode.isWide()) {
            bytecodesBufWriter.writeU2U2(opcode.bytecode(), slot);
        } else {
            bytecodesBufWriter.writeU1U1(opcode.bytecode(), slot);
        }
    }

    // Shortcut version, refer to and validate slot
    private void writeLocalVar(int bytecode, int slot) {
        // TODO validation like (slot & 0xFFFF) == slot
        if (slot < 256) {
            bytecodesBufWriter.writeU1U1(bytecode, slot);
        } else {
            bytecodesBufWriter.writeU1U1U2(WIDE, bytecode, slot);
        }
    }

    public void writeIncrement(boolean wide, int slot, int val) {
        if (wide) {
            bytecodesBufWriter.writeU2U2U2((WIDE << 8) | IINC, slot, val);
        } else {
            bytecodesBufWriter.writeU1U1U1(IINC, slot, val);
        }
    }

    public void writeBranch(Opcode op, Label target) {
        if (op.sizeIfFixed() == 3) {
            writeShortJump(op.bytecode(), target);
        } else {
            writeLongJump(op.bytecode(), target);
        }
    }

    private void writeLongLabelOffset(int instructionPc, Label label) {
        int targetBci = labelToBci(label);

        // algebraic union of jump | (instructionPc, target), distinguished by null == target.
        int jumpOrInstructionPc;
        Label nullOrTarget;
        if (targetBci == -1) {
            jumpOrInstructionPc = instructionPc;
            nullOrTarget = label;
        } else {
            jumpOrInstructionPc = targetBci - instructionPc;
            nullOrTarget = null;
        }

        writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget);
    }

    private void writeShortJump(int bytecode, Label target) {
        int instructionPc = curPc();
        int targetBci = labelToBci(target);

        // algebraic union of jump | (instructionPc, target), distinguished by null == target.
        int jumpOrInstructionPc;
        Label nullOrTarget;
        if (targetBci == -1) {
            jumpOrInstructionPc = instructionPc;
            nullOrTarget = target;
        } else {
            jumpOrInstructionPc = targetBci - instructionPc;
            nullOrTarget = null;
        }

        //transform short-opcode forward jumps if enforced, and backward jumps if enabled and overflowing
        if (transformDeferredJumps || transformKnownJumps && nullOrTarget == null && jumpOrInstructionPc < Short.MIN_VALUE) {
            fixShortJump(bytecode, jumpOrInstructionPc, nullOrTarget);
        } else {
            bytecodesBufWriter.writeU1(bytecode);
            writeParsedShortLabel(jumpOrInstructionPc, nullOrTarget);
        }
    }

    private void writeLongJump(int bytecode, Label target) {
        int instructionPc = curPc();
        bytecodesBufWriter.writeU1(bytecode);
        writeLongLabelOffset(instructionPc, target);
    }

    private void fixShortJump(int bytecode, int jumpOrInstructionPc, Label nullOrTarget) {
        if (bytecode == GOTO) {
            bytecodesBufWriter.writeU1(GOTO_W);
            writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget);
        } else if (bytecode == JSR) {
            bytecodesBufWriter.writeU1(JSR_W);
            writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget);
        } else {
            bytecodesBufWriter.writeU1U2(
                    BytecodeHelpers.reverseBranchOpcode(bytecode),   // u1
                    8); // u1 + s2 + u1 + s4                         // s2
            bytecodesBufWriter.writeU1(GOTO_W);                      // u1
            if (nullOrTarget == null) {
                jumpOrInstructionPc -= 3; // jump -= 3;
            } else {
                jumpOrInstructionPc += 3; // instructionPc += 3;
            }
            writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); // s4
        }
    }

    private void writeParsedShortLabel(int jumpOrInstructionPc, Label nullOrTarget) {
        if (nullOrTarget == null) {
            if ((short) jumpOrInstructionPc != jumpOrInstructionPc)
                throw new LabelOverflowException();
            bytecodesBufWriter.writeU2(jumpOrInstructionPc);
        } else {
            int pc = bytecodesBufWriter.skip(2);
            addLabel(new DeferredLabel(pc, 2, jumpOrInstructionPc, nullOrTarget));
        }
    }

    private void writeParsedLongLabel(int jumpOrInstructionPc, Label nullOrTarget) {
        if (nullOrTarget == null) {
            bytecodesBufWriter.writeInt(jumpOrInstructionPc);
        } else {
            int pc = bytecodesBufWriter.skip(4);
            addLabel(new DeferredLabel(pc, 4, jumpOrInstructionPc, nullOrTarget));
        }
    }

    public void writeLookupSwitch(Label defaultTarget, List cases) {
        int instructionPc = curPc();
        bytecodesBufWriter.writeU1(LOOKUPSWITCH);
        int pad = 4 - (curPc() % 4);
        if (pad != 4)
            bytecodesBufWriter.skip(pad); // padding content can be anything
        writeLongLabelOffset(instructionPc, defaultTarget);
        bytecodesBufWriter.writeInt(cases.size());
        cases = new ArrayList<>(cases);
        cases.sort(new Comparator<>() {
            @Override
            public int compare(SwitchCase c1, SwitchCase c2) {
                return Integer.compare(c1.caseValue(), c2.caseValue());
            }
        });
        for (var c : cases) {
            bytecodesBufWriter.writeInt(c.caseValue());
            var target = c.target();
            writeLongLabelOffset(instructionPc, target);
        }
    }

    public void writeTableSwitch(int low, int high, Label defaultTarget, List cases) {
        int instructionPc = curPc();
        bytecodesBufWriter.writeU1(TABLESWITCH);
        int pad = 4 - (curPc() % 4);
        if (pad != 4)
            bytecodesBufWriter.skip(pad); // padding content can be anything
        writeLongLabelOffset(instructionPc, defaultTarget);
        bytecodesBufWriter.writeIntInt(low, high);
        var caseMap = new HashMap(cases.size());
        for (var c : cases) {
            caseMap.put(c.caseValue(), c.target());
        }
        for (long l = low; l<=high; l++) {
            var target = caseMap.getOrDefault((int)l, defaultTarget);
            writeLongLabelOffset(instructionPc, target);
        }
    }

    public void writeFieldAccess(Opcode opcode, FieldRefEntry ref) {
        bytecodesBufWriter.writeIndex(opcode.bytecode(), ref);
    }

    public void writeInvokeNormal(Opcode opcode, MemberRefEntry ref) {
        bytecodesBufWriter.writeIndex(opcode.bytecode(), ref);
    }

    public void writeInvokeInterface(Opcode opcode,
                                     InterfaceMethodRefEntry ref,
                                     int count) {
        bytecodesBufWriter.writeIndex(opcode.bytecode(), ref);
        bytecodesBufWriter.writeU1U1(count, 0);
    }

    public void writeInvokeDynamic(InvokeDynamicEntry ref) {
        bytecodesBufWriter.writeU1U2U2(INVOKEDYNAMIC, bytecodesBufWriter.cpIndex(ref), 0);
    }

    public void writeNewObject(ClassEntry type) {
        bytecodesBufWriter.writeIndex(NEW, type);
    }

    public void writeNewPrimitiveArray(int newArrayCode) {
        bytecodesBufWriter.writeU1U1(NEWARRAY, newArrayCode);
    }

    public void writeNewReferenceArray(ClassEntry type) {
        bytecodesBufWriter.writeIndex(ANEWARRAY, type);
    }

    public void writeNewMultidimensionalArray(int dimensions, ClassEntry type) {
        bytecodesBufWriter.writeIndex(MULTIANEWARRAY, type);
        bytecodesBufWriter.writeU1(dimensions);
    }

    public void writeTypeCheck(Opcode opcode, ClassEntry type) {
        bytecodesBufWriter.writeIndex(opcode.bytecode(), type);
    }

    public void writeArgumentConstant(Opcode opcode, int value) {
        if (opcode.sizeIfFixed() == 3) {
            bytecodesBufWriter.writeU1U2(opcode.bytecode(), value);
        } else {
            bytecodesBufWriter.writeU1U1(opcode.bytecode(), value);
        }
    }

    // value may not be writable to this constant pool
    public void writeAdaptLoadConstant(Opcode opcode, LoadableConstantEntry value) {
        var pe = AbstractPoolEntry.maybeClone(constantPool, value);
        int index = pe.index();
        if (pe != value && opcode != Opcode.LDC2_W) {
            // rewrite ldc/ldc_w if external entry; ldc2_w never needs rewrites
            opcode = index <= 0xFF ? Opcode.LDC : Opcode.LDC_W;
        }

        writeDirectLoadConstant(opcode, pe);
    }

    // the loadable entry is writable to this constant pool
    public void writeDirectLoadConstant(Opcode opcode, LoadableConstantEntry pe) {
        assert !opcode.isWide() && canWriteDirect(pe.constantPool());
        int index = pe.index();
        if (opcode.sizeIfFixed() == 3) {
            bytecodesBufWriter.writeU1U2(opcode.bytecode(), index);
        } else {
            bytecodesBufWriter.writeU1U1(opcode.bytecode(), index);
        }
    }

    @Override
    public Label getLabel(int bci) {
        throw new UnsupportedOperationException("Lookup by BCI not supported by CodeBuilder");
    }

    @Override
    public int labelToBci(Label label) {
        LabelImpl lab = (LabelImpl) label;
        LabelContext context = lab.labelContext();
        if (context == this) {
            return lab.getBCI();
        }
        return labelToBci(context, lab);
    }

    private int labelToBci(LabelContext context, LabelImpl lab) {
        if (context == mruParent) {
            return mruParentTable[lab.getBCI()] - 1;
        }
        else if (context instanceof CodeAttribute parent) {
            if (parentMap == null)
                parentMap = new IdentityHashMap<>();
            //critical JDK bootstrap path, cannot use lambda here
            int[] table = parentMap.computeIfAbsent(parent, new Function() {
                @Override
                public int[] apply(CodeAttribute x) {
                    return new int[parent.codeLength() + 1];
                }
            });

            mruParent = parent;
            mruParentTable = table;
            return mruParentTable[lab.getBCI()] - 1;
        }
        else if (context instanceof BufferedCodeBuilder) {
            // Hijack the label
            return lab.getBCI();
        }
        else {
            throw new IllegalStateException(String.format("Unexpected label context %s in =%s", context, this));
        }
    }

    public void setLineNumber(int lineNo) {
        if (lineNumberWriter == null)
            lineNumberWriter = new DedupLineNumberTableAttribute(constantPool, context);
        lineNumberWriter.writeLineNumber(curPc(), lineNo);
    }

    public void setLabelTarget(Label label) {
        setLabelTarget(label, curPc());
    }

    @Override
    public void setLabelTarget(Label label, int bci) {
        LabelImpl lab = (LabelImpl) label;
        if (lab.labelContext() == this) {
            if (lab.getBCI() != -1)
                throw new IllegalArgumentException("Setting label target for already-set label");
            lab.setBCI(bci);
        } else {
            setLabelTarget(lab, bci);
        }
    }

    private void setLabelTarget(LabelImpl lab, int bci) {
        LabelContext context = lab.labelContext();
        if (context == mruParent) {
            mruParentTable[lab.getBCI()] = bci + 1;
        }
        else if (context instanceof CodeAttribute parent) {
            if (parentMap == null)
                parentMap = new IdentityHashMap<>();
            int[] table = parentMap.computeIfAbsent(parent, new Function() {
                @Override
                public int[] apply(CodeAttribute x) {
                    return new int[parent.codeLength() + 1];
                }
            });

            mruParent = parent;
            mruParentTable = table;
            table[lab.getBCI()] = bci + 1;
        }
        else if (context instanceof BufferedCodeBuilder) {
            // Hijack the label
            lab.setBCI(bci);
        }
        else {
            throw new IllegalStateException(String.format("Unexpected label context %s in =%s", context, this));
        }
    }

    public void addCharacterRange(CharacterRange element) {
        if (characterRangesCount >= characterRanges.length) {
            int newCapacity = characterRangesCount + 8;
            this.characterRanges = Arrays.copyOf(characterRanges, newCapacity);
        }
        characterRanges[characterRangesCount++] = element;
    }

    public void addLabel(DeferredLabel label) {
        if (deferredLabelsCount >= deferredLabels.length) {
            int newCapacity = deferredLabelsCount + 8;
            this.deferredLabels = Arrays.copyOf(deferredLabels, newCapacity);
        }
        deferredLabels[deferredLabelsCount++] = label;
    }

    public void addHandler(ExceptionCatch element) {
        AbstractPseudoInstruction.ExceptionCatchImpl el = (AbstractPseudoInstruction.ExceptionCatchImpl) element;
        ClassEntry type = el.catchTypeEntry();
        if (type != null && !constantPool.canWriteDirect(type.constantPool()))
            el = new AbstractPseudoInstruction.ExceptionCatchImpl(element.handler(), element.tryStart(), element.tryEnd(), AbstractPoolEntry.maybeClone(constantPool, type));
        handlers.add(el);
    }

    public void addLocalVariable(LocalVariable element) {
        if (localVariablesCount >= localVariables.length) {
            int newCapacity = localVariablesCount + 8;
            this.localVariables = Arrays.copyOf(localVariables, newCapacity);
        }
        localVariables[localVariablesCount++] = element;
    }

    public void addLocalVariableType(LocalVariableType element) {
        if (localVariableTypesCount >= localVariableTypes.length) {
            int newCapacity = localVariableTypesCount + 8;
            this.localVariableTypes = Arrays.copyOf(localVariableTypes, newCapacity);
        }
        localVariableTypes[localVariableTypesCount++] = element;
    }

    @Override
    public String toString() {
        return String.format("CodeBuilder[id=%d]", System.identityHashCode(this));
    }

    //ToDo: consolidate and open all exceptions
    private static final class LabelOverflowException extends IllegalArgumentException {

        private static final long serialVersionUID = 1L;

        public LabelOverflowException() {
            super("Label target offset overflow");
        }
    }

    // Fast overrides to avoid intermediate instructions
    // These are helpful for direct class building

    @Override
    public CodeBuilder return_() {
        bytecodesBufWriter.writeU1(RETURN);
        return this;
    }

    @Override
    public CodeBuilder return_(TypeKind tk) {
        bytecodesBufWriter.writeU1(returnBytecode(tk));
        return this;
    }

    @Override
    public CodeBuilder storeLocal(TypeKind tk, int slot) {
        return switch (tk) {
            case INT, SHORT, BYTE, CHAR, BOOLEAN
                           -> istore(slot);
            case LONG      -> lstore(slot);
            case DOUBLE    -> dstore(slot);
            case FLOAT     -> fstore(slot);
            case REFERENCE -> astore(slot);
            case VOID      -> throw new IllegalArgumentException("void");
        };
    }

    @Override
    public CodeBuilder labelBinding(Label label) {
        setLabelTarget(label, curPc());
        return this;
    }

    @Override
    public CodeBuilder loadLocal(TypeKind tk, int slot) {
        return switch (tk) {
            case INT, SHORT, BYTE, CHAR, BOOLEAN
                           -> iload(slot);
            case LONG      -> lload(slot);
            case DOUBLE    -> dload(slot);
            case FLOAT     -> fload(slot);
            case REFERENCE -> aload(slot);
            case VOID      -> throw new IllegalArgumentException("void");
        };
    }

    @Override
    public CodeBuilder invoke(Opcode opcode, MemberRefEntry ref) {
        if (opcode == Opcode.INVOKEINTERFACE) {
            int slots = Util.parameterSlots(Util.methodTypeSymbol(ref.type())) + 1;
            writeInvokeInterface(opcode, (InterfaceMethodRefEntry) ref, slots);
        } else {
            writeInvokeNormal(opcode, ref);
        }
        return this;
    }

    @Override
    public CodeBuilder invokespecial(ClassDesc owner, String name, MethodTypeDesc type) {
        bytecodesBufWriter.writeIndex(INVOKESPECIAL, constantPool().methodRefEntry(owner, name, type));
        return this;
    }

    @Override
    public CodeBuilder invokestatic(ClassDesc owner, String name, MethodTypeDesc type) {
        bytecodesBufWriter.writeIndex(INVOKESTATIC, constantPool().methodRefEntry(owner, name, type));
        return this;
    }

    @Override
    public CodeBuilder invokevirtual(ClassDesc owner, String name, MethodTypeDesc type) {
        bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, constantPool().methodRefEntry(owner, name, type));
        return this;
    }

    @Override
    public CodeBuilder getfield(ClassDesc owner, String name, ClassDesc type) {
        bytecodesBufWriter.writeIndex(GETFIELD, constantPool().fieldRefEntry(owner, name, type));
        return this;
    }

    @Override
    public CodeBuilder fieldAccess(Opcode opcode, FieldRefEntry ref) {
        bytecodesBufWriter.writeIndex(opcode.bytecode(), ref);
        return this;
    }

    @Override
    public CodeBuilder arrayLoad(TypeKind tk) {
        bytecodesBufWriter.writeU1(BytecodeHelpers.arrayLoadBytecode(tk));
        return this;
    }

    @Override
    public CodeBuilder arrayStore(TypeKind tk) {
        bytecodesBufWriter.writeU1(BytecodeHelpers.arrayStoreBytecode(tk));
        return this;
    }

    @Override
    public CodeBuilder branch(Opcode op, Label target) {
        writeBranch(op, target);
        return this;
    }

    @Override
    public CodeBuilder nop() {
        bytecodesBufWriter.writeU1(NOP);
        return this;
    }

    @Override
    public CodeBuilder aconst_null() {
        bytecodesBufWriter.writeU1(ACONST_NULL);
        return this;
    }

    @Override
    public CodeBuilder aload(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(ALOAD_0 + slot);
        } else {
            writeLocalVar(ALOAD, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder anewarray(ClassEntry entry) {
        writeNewReferenceArray(entry);
        return this;
    }

    @Override
    public CodeBuilder arraylength() {
        bytecodesBufWriter.writeU1(ARRAYLENGTH);
        return this;
    }

    @Override
    public CodeBuilder areturn() {
        bytecodesBufWriter.writeU1(ARETURN);
        return this;
    }

    @Override
    public CodeBuilder astore(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(ASTORE_0 + slot);
        } else {
            writeLocalVar(ASTORE, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder athrow() {
        bytecodesBufWriter.writeU1(ATHROW);
        return this;
    }

    @Override
    public CodeBuilder bipush(int b) {
        BytecodeHelpers.validateBipush(b);
        bytecodesBufWriter.writeU1U1(BIPUSH, b);
        return this;
    }

    @Override
    public CodeBuilder checkcast(ClassEntry type) {
        bytecodesBufWriter.writeIndex(CHECKCAST, type);
        return this;
    }

    @Override
    public CodeBuilder d2f() {
        bytecodesBufWriter.writeU1(D2F);
        return this;
    }

    @Override
    public CodeBuilder d2i() {
        bytecodesBufWriter.writeU1(D2I);
        return this;
    }

    @Override
    public CodeBuilder d2l() {
        bytecodesBufWriter.writeU1(D2L);
        return this;
    }

    @Override
    public CodeBuilder dadd() {
        bytecodesBufWriter.writeU1(DADD);
        return this;
    }

    @Override
    public CodeBuilder dcmpg() {
        bytecodesBufWriter.writeU1(DCMPG);
        return this;
    }

    @Override
    public CodeBuilder dcmpl() {
        bytecodesBufWriter.writeU1(DCMPL);
        return this;
    }

    @Override
    public CodeBuilder dconst_0() {
        bytecodesBufWriter.writeU1(DCONST_0);
        return this;
    }

    @Override
    public CodeBuilder dconst_1() {
        bytecodesBufWriter.writeU1(DCONST_1);
        return this;
    }

    @Override
    public CodeBuilder ddiv() {
        bytecodesBufWriter.writeU1(DDIV);
        return this;
    }

    @Override
    public CodeBuilder dload(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(DLOAD_0 + slot);
        } else {
            writeLocalVar(DLOAD, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder dmul() {
        bytecodesBufWriter.writeU1(DMUL);
        return this;
    }

    @Override
    public CodeBuilder dneg() {
        bytecodesBufWriter.writeU1(DNEG);
        return this;
    }

    @Override
    public CodeBuilder drem() {
        bytecodesBufWriter.writeU1(DREM);
        return this;
    }

    @Override
    public CodeBuilder dreturn() {
        bytecodesBufWriter.writeU1(DRETURN);
        return this;
    }

    @Override
    public CodeBuilder dstore(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(DSTORE_0 + slot);
        } else {
            writeLocalVar(DSTORE, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder dsub() {
        bytecodesBufWriter.writeU1(DSUB);
        return this;
    }

    @Override
    public CodeBuilder dup() {
        bytecodesBufWriter.writeU1(DUP);
        return this;
    }

    @Override
    public CodeBuilder dup2() {
        bytecodesBufWriter.writeU1(DUP2);
        return this;
    }

    @Override
    public CodeBuilder dup2_x1() {
        bytecodesBufWriter.writeU1(DUP2_X1);
        return this;
    }

    @Override
    public CodeBuilder dup2_x2() {
        bytecodesBufWriter.writeU1(DUP2_X2);
        return this;
    }

    @Override
    public CodeBuilder dup_x1() {
        bytecodesBufWriter.writeU1(DUP_X1);
        return this;
    }

    @Override
    public CodeBuilder dup_x2() {
        bytecodesBufWriter.writeU1(DUP_X2);
        return this;
    }

    @Override
    public CodeBuilder f2d() {
        bytecodesBufWriter.writeU1(F2D);
        return this;
    }

    @Override
    public CodeBuilder f2i() {
        bytecodesBufWriter.writeU1(F2I);
        return this;
    }

    @Override
    public CodeBuilder f2l() {
        bytecodesBufWriter.writeU1(F2L);
        return this;
    }

    @Override
    public CodeBuilder fadd() {
        bytecodesBufWriter.writeU1(FADD);
        return this;
    }

    @Override
    public CodeBuilder fcmpg() {
        bytecodesBufWriter.writeU1(FCMPG);
        return this;
    }

    @Override
    public CodeBuilder fcmpl() {
        bytecodesBufWriter.writeU1(FCMPL);
        return this;
    }

    @Override
    public CodeBuilder fconst_0() {
        bytecodesBufWriter.writeU1(FCONST_0);
        return this;
    }

    @Override
    public CodeBuilder fconst_1() {
        bytecodesBufWriter.writeU1(FCONST_1);
        return this;
    }

    @Override
    public CodeBuilder fconst_2() {
        bytecodesBufWriter.writeU1(FCONST_2);
        return this;
    }

    @Override
    public CodeBuilder fdiv() {
        bytecodesBufWriter.writeU1(FDIV);
        return this;
    }

    @Override
    public CodeBuilder fload(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(FLOAD_0 + slot);
        } else {
            writeLocalVar(FLOAD, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder fmul() {
        bytecodesBufWriter.writeU1(FMUL);
        return this;
    }

    @Override
    public CodeBuilder fneg() {
        bytecodesBufWriter.writeU1(FNEG);
        return this;
    }

    @Override
    public CodeBuilder frem() {
        bytecodesBufWriter.writeU1(FREM);
        return this;
    }

    @Override
    public CodeBuilder freturn() {
        bytecodesBufWriter.writeU1(FRETURN);
        return this;
    }

    @Override
    public CodeBuilder fstore(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(FSTORE_0 + slot);
        } else {
            writeLocalVar(FSTORE, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder fsub() {
        bytecodesBufWriter.writeU1(FSUB);
        return this;
    }

    @Override
    public CodeBuilder getstatic(ClassDesc owner, String name, ClassDesc type) {
        bytecodesBufWriter.writeIndex(GETSTATIC, constantPool().fieldRefEntry(owner, name, type));
        return this;
    }

    @Override
    public CodeBuilder goto_(Label target) {
        writeShortJump(GOTO, target);
        return this;
    }

    @Override
    public CodeBuilder i2b() {
        bytecodesBufWriter.writeU1(I2B);
        return this;
    }

    @Override
    public CodeBuilder i2c() {
        bytecodesBufWriter.writeU1(I2C);
        return this;
    }

    @Override
    public CodeBuilder i2d() {
        bytecodesBufWriter.writeU1(I2D);
        return this;
    }

    @Override
    public CodeBuilder i2f() {
        bytecodesBufWriter.writeU1(I2F);
        return this;
    }

    @Override
    public CodeBuilder i2l() {
        bytecodesBufWriter.writeU1(I2L);
        return this;
    }

    @Override
    public CodeBuilder i2s() {
        bytecodesBufWriter.writeU1(I2S);
        return this;
    }

    @Override
    public CodeBuilder iadd() {
        bytecodesBufWriter.writeU1(IADD);
        return this;
    }

    @Override
    public CodeBuilder iand() {
        bytecodesBufWriter.writeU1(IAND);
        return this;
    }

    @Override
    public CodeBuilder iconst_0() {
        bytecodesBufWriter.writeU1(ICONST_0);
        return this;
    }

    @Override
    public CodeBuilder iconst_1() {
        bytecodesBufWriter.writeU1(ICONST_1);
        return this;
    }

    @Override
    public CodeBuilder iconst_2() {
        bytecodesBufWriter.writeU1(ICONST_2);
        return this;
    }

    @Override
    public CodeBuilder iconst_3() {
        bytecodesBufWriter.writeU1(ICONST_3);
        return this;
    }

    @Override
    public CodeBuilder iconst_4() {
        bytecodesBufWriter.writeU1(ICONST_4);
        return this;
    }

    @Override
    public CodeBuilder iconst_5() {
        bytecodesBufWriter.writeU1(ICONST_5);
        return this;
    }

    @Override
    public CodeBuilder iconst_m1() {
        bytecodesBufWriter.writeU1(ICONST_M1);
        return this;
    }

    @Override
    public CodeBuilder idiv() {
        bytecodesBufWriter.writeU1(IDIV);
        return this;
    }

    @Override
    public CodeBuilder if_acmpeq(Label target) {
        writeShortJump(IF_ACMPEQ, target);
        return this;
    }

    @Override
    public CodeBuilder if_acmpne(Label target) {
        writeShortJump(IF_ACMPNE, target);
        return this;
    }

    @Override
    public CodeBuilder if_icmpeq(Label target) {
        writeShortJump(IF_ICMPEQ, target);
        return this;
    }

    @Override
    public CodeBuilder if_icmpge(Label target) {
        writeShortJump(IF_ICMPGE, target);
        return this;
    }

    @Override
    public CodeBuilder if_icmpgt(Label target) {
        writeShortJump(IF_ICMPGT, target);
        return this;
    }

    @Override
    public CodeBuilder if_icmple(Label target) {
        writeShortJump(IF_ICMPLE, target);
        return this;
    }

    @Override
    public CodeBuilder if_icmplt(Label target) {
        writeShortJump(IF_ICMPLT, target);
        return this;
    }

    @Override
    public CodeBuilder if_icmpne(Label target) {
        writeShortJump(IF_ICMPNE, target);
        return this;
    }

    @Override
    public CodeBuilder ifnonnull(Label target) {
        writeShortJump(IFNONNULL, target);
        return this;
    }

    @Override
    public CodeBuilder ifnull(Label target) {
        writeShortJump(IFNULL, target);
        return this;
    }

    @Override
    public CodeBuilder ifeq(Label target) {
        writeShortJump(IFEQ, target);
        return this;
    }

    @Override
    public CodeBuilder ifge(Label target) {
        writeShortJump(IFGE, target);
        return this;
    }

    @Override
    public CodeBuilder ifgt(Label target) {
        writeShortJump(IFGT, target);
        return this;
    }

    @Override
    public CodeBuilder ifle(Label target) {
        writeShortJump(IFLE, target);
        return this;
    }

    @Override
    public CodeBuilder iflt(Label target) {
        writeShortJump(IFLT, target);
        return this;
    }

    @Override
    public CodeBuilder ifne(Label target) {
        writeShortJump(IFNE, target);
        return this;
    }

    @Override
    public CodeBuilder iinc(int slot, int val) {
        writeIncrement(validateAndIsWideIinc(slot, val), slot, val);
        return this;
    }

    @Override
    public CodeBuilder iload(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(ILOAD_0 + slot);
        } else {
            writeLocalVar(ILOAD, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder imul() {
        bytecodesBufWriter.writeU1(IMUL);
        return this;
    }

    @Override
    public CodeBuilder ineg() {
        bytecodesBufWriter.writeU1(INEG);
        return this;
    }

    @Override
    public CodeBuilder instanceOf(ClassEntry target) {
        bytecodesBufWriter.writeIndex(INSTANCEOF, target);
        return this;
    }

    @Override
    public CodeBuilder invokedynamic(InvokeDynamicEntry ref) {
        writeInvokeDynamic(ref);
        return this;
    }

    @Override
    public CodeBuilder invokeinterface(InterfaceMethodRefEntry ref) {
        writeInvokeInterface(Opcode.INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1);
        return this;
    }

    @Override
    public CodeBuilder invokespecial(InterfaceMethodRefEntry ref) {
        bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref);
        return this;
    }

    @Override
    public CodeBuilder invokespecial(MethodRefEntry ref) {
        bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref);
        return this;
    }

    @Override
    public CodeBuilder invokestatic(InterfaceMethodRefEntry ref) {
        bytecodesBufWriter.writeIndex(INVOKESTATIC, ref);
        return this;
    }

    @Override
    public CodeBuilder invokestatic(MethodRefEntry ref) {
        bytecodesBufWriter.writeIndex(INVOKESTATIC, ref);
        return this;
    }

    @Override
    public CodeBuilder invokevirtual(MethodRefEntry ref) {
        bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, ref);
        return this;
    }

    @Override
    public CodeBuilder ior() {
        bytecodesBufWriter.writeU1(IOR);
        return this;
    }

    @Override
    public CodeBuilder irem() {
        bytecodesBufWriter.writeU1(IREM);
        return this;
    }

    @Override
    public CodeBuilder ireturn() {
        bytecodesBufWriter.writeU1(IRETURN);
        return this;
    }

    @Override
    public CodeBuilder ishl() {
        bytecodesBufWriter.writeU1(ISHL);
        return this;
    }

    @Override
    public CodeBuilder ishr() {
        bytecodesBufWriter.writeU1(ISHR);
        return this;
    }

    @Override
    public CodeBuilder istore(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(ISTORE_0 + slot);
        } else {
            writeLocalVar(ISTORE, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder isub() {
        bytecodesBufWriter.writeU1(ISUB);
        return this;
    }

    @Override
    public CodeBuilder iushr() {
        bytecodesBufWriter.writeU1(IUSHR);
        return this;
    }

    @Override
    public CodeBuilder ixor() {
        bytecodesBufWriter.writeU1(IXOR);
        return this;
    }

    @Override
    public CodeBuilder lookupswitch(Label defaultTarget, List cases) {
        writeLookupSwitch(defaultTarget, cases);
        return this;
    }

    @Override
    public CodeBuilder l2d() {
        bytecodesBufWriter.writeU1(L2D);
        return this;
    }

    @Override
    public CodeBuilder l2f() {
        bytecodesBufWriter.writeU1(L2F);
        return this;
    }

    @Override
    public CodeBuilder l2i() {
        bytecodesBufWriter.writeU1(L2I);
        return this;
    }

    @Override
    public CodeBuilder ladd() {
        bytecodesBufWriter.writeU1(LADD);
        return this;
    }

    @Override
    public CodeBuilder land() {
        bytecodesBufWriter.writeU1(LAND);
        return this;
    }

    @Override
    public CodeBuilder lcmp() {
        bytecodesBufWriter.writeU1(LCMP);
        return this;
    }

    @Override
    public CodeBuilder lconst_0() {
        bytecodesBufWriter.writeU1(LCONST_0);
        return this;
    }

    @Override
    public CodeBuilder lconst_1() {
        bytecodesBufWriter.writeU1(LCONST_1);
        return this;
    }

    @Override
    public CodeBuilder ldc(LoadableConstantEntry entry) {
        var direct = AbstractPoolEntry.maybeClone(constantPool, entry);
        writeDirectLoadConstant(BytecodeHelpers.ldcOpcode(direct), direct);
        return this;
    }

    @Override
    public CodeBuilder ldiv() {
        bytecodesBufWriter.writeU1(LDIV);
        return this;
    }

    @Override
    public CodeBuilder lload(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(LLOAD_0 + slot);
        } else {
            writeLocalVar(LLOAD, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder lmul() {
        bytecodesBufWriter.writeU1(LMUL);
        return this;
    }

    @Override
    public CodeBuilder lneg() {
        bytecodesBufWriter.writeU1(LNEG);
        return this;
    }

    @Override
    public CodeBuilder lor() {
        bytecodesBufWriter.writeU1(LOR);
        return this;
    }

    @Override
    public CodeBuilder lrem() {
        bytecodesBufWriter.writeU1(LREM);
        return this;
    }

    @Override
    public CodeBuilder lreturn() {
        bytecodesBufWriter.writeU1(LRETURN);
        return this;
    }

    @Override
    public CodeBuilder lshl() {
        bytecodesBufWriter.writeU1(LSHL);
        return this;
    }

    @Override
    public CodeBuilder lshr() {
        bytecodesBufWriter.writeU1(LSHR);
        return this;
    }

    @Override
    public CodeBuilder lstore(int slot) {
        if (slot >= 0 && slot <= 3) {
            bytecodesBufWriter.writeU1(LSTORE_0 + slot);
        } else {
            writeLocalVar(LSTORE, slot);
        }
        return this;
    }

    @Override
    public CodeBuilder lsub() {
        bytecodesBufWriter.writeU1(LSUB);
        return this;
    }

    @Override
    public CodeBuilder lushr() {
        bytecodesBufWriter.writeU1(LUSHR);
        return this;
    }

    @Override
    public CodeBuilder lxor() {
        bytecodesBufWriter.writeU1(LXOR);
        return this;
    }

    @Override
    public CodeBuilder monitorenter() {
        bytecodesBufWriter.writeU1(MONITORENTER);
        return this;
    }

    @Override
    public CodeBuilder monitorexit() {
        bytecodesBufWriter.writeU1(MONITOREXIT);
        return this;
    }

    @Override
    public CodeBuilder multianewarray(ClassEntry array, int dims) {
        writeNewMultidimensionalArray(dims, array);
        return this;
    }

    @Override
    public CodeBuilder new_(ClassEntry clazz) {
        writeNewObject(clazz);
        return this;
    }

    @Override
    public CodeBuilder newarray(TypeKind typeKind) {
        int atype = typeKind.newarrayCode(); // implicit null check
        if (atype < 0)
            throw new IllegalArgumentException("Illegal component type: ".concat(typeKind.upperBound().displayName()));
        writeNewPrimitiveArray(atype);
        return this;
    }

    @Override
    public CodeBuilder pop() {
        bytecodesBufWriter.writeU1(POP);
        return this;
    }

    @Override
    public CodeBuilder pop2() {
        bytecodesBufWriter.writeU1(POP2);
        return this;
    }

    @Override
    public CodeBuilder sipush(int s) {
        BytecodeHelpers.validateSipush(s);
        bytecodesBufWriter.writeU1U2(SIPUSH, s);
        return this;
    }

    @Override
    public CodeBuilder swap() {
        bytecodesBufWriter.writeU1(SWAP);
        return this;
    }

    @Override
    public CodeBuilder tableswitch(int low, int high, Label defaultTarget, List cases) {
        writeTableSwitch(low, high, defaultTarget, cases);
        return this;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy