io.github.dmlloyd.classfile.impl.DirectCodeBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdk-classfile-backport Show documentation
Show all versions of jdk-classfile-backport Show documentation
An unofficial backport of the JDK Classfile API to Java 17
/*
* 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 super CodeBuilder> 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