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-preview Show documentation
Show all versions of jdk-classfile-preview 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.
* 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.Attribute;
import io.github.dmlloyd.classfile.Attributes;
import io.github.dmlloyd.classfile.ClassFile;
import io.github.dmlloyd.classfile.CodeBuilder;
import io.github.dmlloyd.classfile.CodeElement;
import io.github.dmlloyd.classfile.CodeModel;
import io.github.dmlloyd.classfile.CustomAttribute;
import io.github.dmlloyd.classfile.Label;
import io.github.dmlloyd.classfile.Opcode;
import io.github.dmlloyd.classfile.TypeKind;
import io.github.dmlloyd.classfile.attribute.CodeAttribute;
import io.github.dmlloyd.classfile.attribute.LineNumberTableAttribute;
import io.github.dmlloyd.classfile.constantpool.ClassEntry;
import io.github.dmlloyd.classfile.constantpool.ConstantPoolBuilder;
import io.github.dmlloyd.classfile.constantpool.DoubleEntry;
import io.github.dmlloyd.classfile.constantpool.FieldRefEntry;
import io.github.dmlloyd.classfile.constantpool.InterfaceMethodRefEntry;
import io.github.dmlloyd.classfile.constantpool.InvokeDynamicEntry;
import io.github.dmlloyd.classfile.constantpool.LoadableConstantEntry;
import io.github.dmlloyd.classfile.constantpool.LongEntry;
import io.github.dmlloyd.classfile.constantpool.MemberRefEntry;
import io.github.dmlloyd.classfile.constantpool.MethodRefEntry;
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.ConstantDesc;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import static io.github.dmlloyd.classfile.Opcode.*;
public final class DirectCodeBuilder
extends AbstractDirectBuilder
implements TerminalCodeBuilder {
private final List characterRanges = new ArrayList<>();
final List handlers = new ArrayList<>();
private final List localVariables = new ArrayList<>();
private final List localVariableTypes = new ArrayList<>();
private final boolean transformFwdJumps, transformBackJumps;
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;
List deferredLabels;
/* 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.shortJumpsOption() == ClassFile.ShortJumpsOption.FIX_SHORT_JUMPS) {
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 transformFwdJumps) {
super(constantPool, context);
setOriginal(original);
this.methodInfo = methodInfo;
this.transformFwdJumps = transformFwdJumps;
this.transformBackJumps = context.shortJumpsOption() == ClassFile.ShortJumpsOption.FIX_SHORT_JUMPS;
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>) 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);
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.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) {
handlersSize--;
} else {
throw new IllegalArgumentException("Unbound label in exception handler");
}
} else {
buf.writeU2(startPc);
buf.writeU2(endPc);
buf.writeU2(handlerPc);
buf.writeIndexOrZero(h.catchTypeEntry());
handlersSize++;
}
}
if (handlersSize < handlers.size())
buf.patchInt(pos, 2, handlersSize);
}
private void buildContent() {
if (content != null) return;
setLabelTarget(endLabel);
// Backfill branches for which Label didn't have position yet
processDeferredLabels();
if (context.debugElementsOption() == ClassFile.DebugElementsOption.PASS_DEBUG) {
if (!characterRanges.isEmpty()) {
Attribute> a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) {
@Override
public void writeBody(BufWriterImpl b) {
int pos = b.size();
int crSize = characterRanges.size();
b.writeU2(crSize);
for (CharacterRange cr : characterRanges) {
var start = labelToBci(cr.startScope());
var end = labelToBci(cr.endScope());
if (start == -1 || end == -1) {
if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) {
crSize--;
} else {
throw new IllegalArgumentException("Unbound label in character range");
}
} else {
b.writeU2(start);
b.writeU2(end - 1);
b.writeInt(cr.characterRangeStart());
b.writeInt(cr.characterRangeEnd());
b.writeU2(cr.flags());
}
}
if (crSize < characterRanges.size())
b.patchInt(pos, 2, crSize);
}
};
attributes.withAttribute(a);
}
if (!localVariables.isEmpty()) {
Attribute> a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTable()) {
@Override
public void writeBody(BufWriterImpl b) {
int pos = b.size();
int lvSize = localVariables.size();
b.writeU2(lvSize);
for (LocalVariable l : localVariables) {
if (!Util.writeLocalVariable(b, l)) {
if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) {
lvSize--;
} else {
throw new IllegalArgumentException("Unbound label in local variable type");
}
}
}
if (lvSize < localVariables.size())
b.patchInt(pos, 2, lvSize);
}
};
attributes.withAttribute(a);
}
if (!localVariableTypes.isEmpty()) {
Attribute> a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTypeTable()) {
@Override
public void writeBody(BufWriterImpl b) {
int pos = b.size();
int lvtSize = localVariableTypes.size();
b.writeU2(localVariableTypes.size());
for (LocalVariableType l : localVariableTypes) {
if (!Util.writeLocalVariable(b, l)) {
if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) {
lvtSize--;
} else {
throw new IllegalArgumentException("Unbound label in local variable type");
}
}
}
if (lvtSize < localVariableTypes.size())
b.patchInt(pos, 2, lvtSize);
}
};
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.writeU2(originalAttribute.maxStack());
buf.writeU2(originalAttribute.maxLocals());
} else {
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
buf.writeU2(cntr.maxStack());
buf.writeU2(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
StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf);
attributes.withAttribute(gen.stackMapTableAttribute());
buf.writeU2(gen.maxStack());
buf.writeU2(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) {
buf.setLabelContext(DirectCodeBuilder.this);
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,
methodInfo.methodName().stringValue(),
methodInfo.methodTypeSymbol().displayDescriptor()));
}
if (codeAndExceptionsMatch(codeLength)) {
switch (context.stackMapsOption()) {
case STACK_MAPS_WHEN_REQUIRED -> {
attributes.withAttribute(original.findAttribute(Attributes.stackMapTable()).orElse(null));
writeCounters(true, buf);
}
case GENERATE_STACK_MAPS ->
generateStackMaps(buf);
case DROP_STACK_MAPS ->
writeCounters(true, buf);
}
} else {
switch (context.stackMapsOption()) {
case STACK_MAPS_WHEN_REQUIRED ->
tryGenerateStackMaps(false, buf);
case GENERATE_STACK_MAPS ->
generateStackMaps(buf);
case DROP_STACK_MAPS ->
writeCounters(false, buf);
}
}
buf.writeInt(codeLength);
buf.writeBytes(bytecodesBufWriter);
writeExceptionHandlers(buf);
attributes.writeTo(buf);
buf.setLabelContext(null);
}
};
}
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.writeU2(lastPc);
buf.writeU2(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);
}
}
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 writeLabelOffset(int nBytes, int instructionPc, Label label) {
int targetBci = labelToBci(label);
if (targetBci == -1) {
int pc = curPc();
bytecodesBufWriter.writeIntBytes(nBytes, 0);
if (deferredLabels == null)
deferredLabels = new ArrayList<>();
deferredLabels.add(new DeferredLabel(pc, nBytes, instructionPc, label));
}
else {
int branchOffset = targetBci - instructionPc;
if (nBytes == 2 && (short)branchOffset != branchOffset) throw new LabelOverflowException();
bytecodesBufWriter.writeIntBytes(nBytes, branchOffset);
}
}
private void processDeferredLabels() {
if (deferredLabels != null) {
for (DeferredLabel dl : deferredLabels) {
int branchOffset = labelToBci(dl.label) - dl.instructionPc;
if (dl.size == 2 && (short)branchOffset != branchOffset) throw new LabelOverflowException();
bytecodesBufWriter.patchInt(dl.labelPc, dl.size, branchOffset);
}
}
}
// Instruction writing
public void writeBytecode(Opcode opcode) {
if (opcode.isWide())
bytecodesBufWriter.writeU1(ClassFile.WIDE);
bytecodesBufWriter.writeU1(opcode.bytecode() & 0xFF);
}
public void writeLocalVar(Opcode opcode, int localVar) {
writeBytecode(opcode);
switch (opcode.sizeIfFixed()) {
case 1 -> { }
case 2 -> bytecodesBufWriter.writeU1(localVar);
case 4 -> bytecodesBufWriter.writeU2(localVar);
default -> throw new IllegalArgumentException("Unexpected instruction size: " + opcode);
}
}
public void writeIncrement(int slot, int val) {
Opcode opcode = (slot < 256 && val < 128 && val > -127)
? IINC
: IINC_W;
writeBytecode(opcode);
if (opcode.isWide()) {
bytecodesBufWriter.writeU2(slot);
bytecodesBufWriter.writeU2(val);
} else {
bytecodesBufWriter.writeU1(slot);
bytecodesBufWriter.writeU1(val);
}
}
public void writeBranch(Opcode op, Label target) {
int instructionPc = curPc();
int targetBci = labelToBci(target);
//transform short-opcode forward jumps if enforced, and backward jumps if enabled and overflowing
if (op.sizeIfFixed() == 3 && (targetBci == -1
? transformFwdJumps
: (transformBackJumps
&& targetBci - instructionPc < Short.MIN_VALUE))) {
if (op == GOTO) {
writeBytecode(GOTO_W);
writeLabelOffset(4, instructionPc, target);
} else if (op == JSR) {
writeBytecode(JSR_W);
writeLabelOffset(4, instructionPc, target);
} else {
writeBytecode(BytecodeHelpers.reverseBranchOpcode(op));
Label bypassJump = newLabel();
writeLabelOffset(2, instructionPc, bypassJump);
writeBytecode(GOTO_W);
writeLabelOffset(4, instructionPc + 3, target);
labelBinding(bypassJump);
}
} else {
writeBytecode(op);
writeLabelOffset(op.sizeIfFixed() == 3 ? 2 : 4, instructionPc, target);
}
}
public void writeLookupSwitch(Label defaultTarget, List cases) {
int instructionPc = curPc();
writeBytecode(LOOKUPSWITCH);
int pad = 4 - (curPc() % 4);
if (pad != 4)
bytecodesBufWriter.writeIntBytes(pad, 0);
writeLabelOffset(4, 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());
writeLabelOffset(4, instructionPc, c.target());
}
}
public void writeTableSwitch(int low, int high, Label defaultTarget, List cases) {
int instructionPc = curPc();
writeBytecode(TABLESWITCH);
int pad = 4 - (curPc() % 4);
if (pad != 4)
bytecodesBufWriter.writeIntBytes(pad, 0);
writeLabelOffset(4, instructionPc, defaultTarget);
bytecodesBufWriter.writeInt(low);
bytecodesBufWriter.writeInt(high);
var caseMap = new HashMap(cases.size());
for (var c : cases) {
caseMap.put(c.caseValue(), c.target());
}
for (long l = low; l<=high; l++) {
writeLabelOffset(4, instructionPc, caseMap.getOrDefault((int)l, defaultTarget));
}
}
public void writeFieldAccess(Opcode opcode, FieldRefEntry ref) {
writeBytecode(opcode);
bytecodesBufWriter.writeIndex(ref);
}
public void writeInvokeNormal(Opcode opcode, MemberRefEntry ref) {
writeBytecode(opcode);
bytecodesBufWriter.writeIndex(ref);
}
public void writeInvokeInterface(Opcode opcode,
InterfaceMethodRefEntry ref,
int count) {
writeBytecode(opcode);
bytecodesBufWriter.writeIndex(ref);
bytecodesBufWriter.writeU1(count);
bytecodesBufWriter.writeU1(0);
}
public void writeInvokeDynamic(InvokeDynamicEntry ref) {
writeBytecode(INVOKEDYNAMIC);
bytecodesBufWriter.writeIndex(ref);
bytecodesBufWriter.writeU2(0);
}
public void writeNewObject(ClassEntry type) {
writeBytecode(NEW);
bytecodesBufWriter.writeIndex(type);
}
public void writeNewPrimitiveArray(int newArrayCode) {
writeBytecode(NEWARRAY);
bytecodesBufWriter.writeU1(newArrayCode);
}
public void writeNewReferenceArray(ClassEntry type) {
writeBytecode(ANEWARRAY);
bytecodesBufWriter.writeIndex(type);
}
public void writeNewMultidimensionalArray(int dimensions, ClassEntry type) {
writeBytecode(MULTIANEWARRAY);
bytecodesBufWriter.writeIndex(type);
bytecodesBufWriter.writeU1(dimensions);
}
public void writeTypeCheck(Opcode opcode, ClassEntry type) {
writeBytecode(opcode);
bytecodesBufWriter.writeIndex(type);
}
public void writeArgumentConstant(Opcode opcode, int value) {
writeBytecode(opcode);
if (opcode.sizeIfFixed() == 3) {
bytecodesBufWriter.writeU2(value);
} else {
bytecodesBufWriter.writeU1(value);
}
}
public void writeLoadConstant(Opcode opcode, LoadableConstantEntry value) {
// Make sure Long and Double have LDC2_W and
// rewrite to _W if index is > 256
int index = AbstractPoolEntry.maybeClone(constantPool, value).index();
Opcode op = opcode;
if (value instanceof LongEntry || value instanceof DoubleEntry) {
op = LDC2_W;
} else if (index >= 256)
op = LDC_W;
writeBytecode(op);
if (op.sizeIfFixed() == 3) {
bytecodesBufWriter.writeU2(index);
} else {
bytecodesBufWriter.writeU1(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();
}
else 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;
LabelContext context = lab.labelContext();
if (context == this) {
if (lab.getBCI() != -1)
throw new IllegalArgumentException("Setting label target for already-set label");
lab.setBCI(bci);
}
else 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;
mruParentTable[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) {
characterRanges.add(element);
}
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) {
localVariables.add(element);
}
public void addLocalVariableType(LocalVariableType element) {
localVariableTypes.add(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_(TypeKind tk) {
writeBytecode(BytecodeHelpers.returnOpcode(tk));
return this;
}
@Override
public CodeBuilder storeLocal(TypeKind tk, int slot) {
writeLocalVar(BytecodeHelpers.storeOpcode(tk, slot), slot);
return this;
}
@Override
public CodeBuilder loadLocal(TypeKind tk, int slot) {
writeLocalVar(BytecodeHelpers.loadOpcode(tk, slot), slot);
return this;
}
@Override
public CodeBuilder invoke(Opcode opcode, MemberRefEntry ref) {
if (opcode == INVOKEINTERFACE) {
int slots = Util.parameterSlots(Util.methodTypeSymbol(ref.nameAndType())) + 1;
writeInvokeInterface(opcode, (InterfaceMethodRefEntry) ref, slots);
} else {
writeInvokeNormal(opcode, ref);
}
return this;
}
@Override
public CodeBuilder fieldAccess(Opcode opcode, FieldRefEntry ref) {
writeFieldAccess(opcode, ref);
return this;
}
@Override
public CodeBuilder arrayLoad(TypeKind tk) {
writeBytecode(BytecodeHelpers.arrayLoadOpcode(tk));
return this;
}
@Override
public CodeBuilder arrayStore(TypeKind tk) {
writeBytecode(BytecodeHelpers.arrayStoreOpcode(tk));
return this;
}
@Override
public CodeBuilder branch(Opcode op, Label target) {
writeBranch(op, target);
return this;
}
@Override
public CodeBuilder loadConstant(Opcode opcode, ConstantDesc value) {
BytecodeHelpers.validateValue(opcode, value);
// avoid non-local enum switch for bootstrap
if (opcode == BIPUSH || opcode == SIPUSH) {
writeArgumentConstant(opcode, ((Number) value).intValue());
} else if (opcode == LDC || opcode == LDC_W || opcode == LDC2_W) {
writeLoadConstant(opcode, BytecodeHelpers.constantEntry(constantPool(), value));
} else {
// intrinsics
writeBytecode(opcode);
}
return this;
}
@Override
public CodeBuilder nop() {
writeBytecode(NOP);
return this;
}
@Override
public CodeBuilder aconst_null() {
writeBytecode(ACONST_NULL);
return this;
}
@Override
public CodeBuilder anewarray(ClassEntry entry) {
writeNewReferenceArray(entry);
return this;
}
@Override
public CodeBuilder arraylength() {
writeBytecode(ARRAYLENGTH);
return this;
}
@Override
public CodeBuilder athrow() {
writeBytecode(ATHROW);
return this;
}
@Override
public CodeBuilder bipush(int b) {
BytecodeHelpers.validateBipush(b);
writeArgumentConstant(BIPUSH, b);
return this;
}
@Override
public CodeBuilder checkcast(ClassEntry type) {
writeTypeCheck(CHECKCAST, type);
return this;
}
@Override
public CodeBuilder d2f() {
writeBytecode(D2F);
return this;
}
@Override
public CodeBuilder d2i() {
writeBytecode(D2I);
return this;
}
@Override
public CodeBuilder d2l() {
writeBytecode(D2L);
return this;
}
@Override
public CodeBuilder dadd() {
writeBytecode(DADD);
return this;
}
@Override
public CodeBuilder dcmpg() {
writeBytecode(DCMPG);
return this;
}
@Override
public CodeBuilder dcmpl() {
writeBytecode(DCMPL);
return this;
}
@Override
public CodeBuilder dconst_0() {
writeBytecode(DCONST_0);
return this;
}
@Override
public CodeBuilder dconst_1() {
writeBytecode(DCONST_1);
return this;
}
@Override
public CodeBuilder ddiv() {
writeBytecode(DDIV);
return this;
}
@Override
public CodeBuilder dmul() {
writeBytecode(DMUL);
return this;
}
@Override
public CodeBuilder dneg() {
writeBytecode(DNEG);
return this;
}
@Override
public CodeBuilder drem() {
writeBytecode(DREM);
return this;
}
@Override
public CodeBuilder dsub() {
writeBytecode(DSUB);
return this;
}
@Override
public CodeBuilder dup() {
writeBytecode(DUP);
return this;
}
@Override
public CodeBuilder dup2() {
writeBytecode(DUP2);
return this;
}
@Override
public CodeBuilder dup2_x1() {
writeBytecode(DUP2_X1);
return this;
}
@Override
public CodeBuilder dup2_x2() {
writeBytecode(DUP2_X2);
return this;
}
@Override
public CodeBuilder dup_x1() {
writeBytecode(DUP_X1);
return this;
}
@Override
public CodeBuilder dup_x2() {
writeBytecode(DUP_X2);
return this;
}
@Override
public CodeBuilder f2d() {
writeBytecode(F2D);
return this;
}
@Override
public CodeBuilder f2i() {
writeBytecode(F2I);
return this;
}
@Override
public CodeBuilder f2l() {
writeBytecode(F2L);
return this;
}
@Override
public CodeBuilder fadd() {
writeBytecode(FADD);
return this;
}
@Override
public CodeBuilder fcmpg() {
writeBytecode(FCMPG);
return this;
}
@Override
public CodeBuilder fcmpl() {
writeBytecode(FCMPL);
return this;
}
@Override
public CodeBuilder fconst_0() {
writeBytecode(FCONST_0);
return this;
}
@Override
public CodeBuilder fconst_1() {
writeBytecode(FCONST_1);
return this;
}
@Override
public CodeBuilder fconst_2() {
writeBytecode(FCONST_2);
return this;
}
@Override
public CodeBuilder fdiv() {
writeBytecode(FDIV);
return this;
}
@Override
public CodeBuilder fmul() {
writeBytecode(FMUL);
return this;
}
@Override
public CodeBuilder fneg() {
writeBytecode(FNEG);
return this;
}
@Override
public CodeBuilder frem() {
writeBytecode(FREM);
return this;
}
@Override
public CodeBuilder fsub() {
writeBytecode(FSUB);
return this;
}
@Override
public CodeBuilder i2b() {
writeBytecode(I2B);
return this;
}
@Override
public CodeBuilder i2c() {
writeBytecode(I2C);
return this;
}
@Override
public CodeBuilder i2d() {
writeBytecode(I2D);
return this;
}
@Override
public CodeBuilder i2f() {
writeBytecode(I2F);
return this;
}
@Override
public CodeBuilder i2l() {
writeBytecode(I2L);
return this;
}
@Override
public CodeBuilder i2s() {
writeBytecode(I2S);
return this;
}
@Override
public CodeBuilder iadd() {
writeBytecode(IADD);
return this;
}
@Override
public CodeBuilder iand() {
writeBytecode(IAND);
return this;
}
@Override
public CodeBuilder iconst_0() {
writeBytecode(ICONST_0);
return this;
}
@Override
public CodeBuilder iconst_1() {
writeBytecode(ICONST_1);
return this;
}
@Override
public CodeBuilder iconst_2() {
writeBytecode(ICONST_2);
return this;
}
@Override
public CodeBuilder iconst_3() {
writeBytecode(ICONST_3);
return this;
}
@Override
public CodeBuilder iconst_4() {
writeBytecode(ICONST_4);
return this;
}
@Override
public CodeBuilder iconst_5() {
writeBytecode(ICONST_5);
return this;
}
@Override
public CodeBuilder iconst_m1() {
writeBytecode(ICONST_M1);
return this;
}
@Override
public CodeBuilder idiv() {
writeBytecode(IDIV);
return this;
}
@Override
public CodeBuilder iinc(int slot, int val) {
writeIncrement(slot, val);
return this;
}
@Override
public CodeBuilder imul() {
writeBytecode(IMUL);
return this;
}
@Override
public CodeBuilder ineg() {
writeBytecode(INEG);
return this;
}
@Override
public CodeBuilder instanceOf(ClassEntry target) {
writeTypeCheck(INSTANCEOF, target);
return this;
}
@Override
public CodeBuilder invokedynamic(InvokeDynamicEntry ref) {
writeInvokeDynamic(ref);
return this;
}
@Override
public CodeBuilder invokeinterface(InterfaceMethodRefEntry ref) {
writeInvokeInterface(INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1);
return this;
}
@Override
public CodeBuilder invokespecial(InterfaceMethodRefEntry ref) {
writeInvokeNormal(INVOKESPECIAL, ref);
return this;
}
@Override
public CodeBuilder invokespecial(MethodRefEntry ref) {
writeInvokeNormal(INVOKESPECIAL, ref);
return this;
}
@Override
public CodeBuilder invokestatic(InterfaceMethodRefEntry ref) {
writeInvokeNormal(INVOKESTATIC, ref);
return this;
}
@Override
public CodeBuilder invokestatic(MethodRefEntry ref) {
writeInvokeNormal(INVOKESTATIC, ref);
return this;
}
@Override
public CodeBuilder invokevirtual(MethodRefEntry ref) {
writeInvokeNormal(INVOKEVIRTUAL, ref);
return this;
}
@Override
public CodeBuilder ior() {
writeBytecode(IOR);
return this;
}
@Override
public CodeBuilder irem() {
writeBytecode(IREM);
return this;
}
@Override
public CodeBuilder ishl() {
writeBytecode(ISHL);
return this;
}
@Override
public CodeBuilder ishr() {
writeBytecode(ISHR);
return this;
}
@Override
public CodeBuilder isub() {
writeBytecode(ISUB);
return this;
}
@Override
public CodeBuilder iushr() {
writeBytecode(IUSHR);
return this;
}
@Override
public CodeBuilder ixor() {
writeBytecode(IXOR);
return this;
}
@Override
public CodeBuilder lookupswitch(Label defaultTarget, List cases) {
writeLookupSwitch(defaultTarget, cases);
return this;
}
@Override
public CodeBuilder l2d() {
writeBytecode(L2D);
return this;
}
@Override
public CodeBuilder l2f() {
writeBytecode(L2F);
return this;
}
@Override
public CodeBuilder l2i() {
writeBytecode(L2I);
return this;
}
@Override
public CodeBuilder ladd() {
writeBytecode(LADD);
return this;
}
@Override
public CodeBuilder land() {
writeBytecode(LAND);
return this;
}
@Override
public CodeBuilder lcmp() {
writeBytecode(LCMP);
return this;
}
@Override
public CodeBuilder lconst_0() {
writeBytecode(LCONST_0);
return this;
}
@Override
public CodeBuilder lconst_1() {
writeBytecode(LCONST_1);
return this;
}
@Override
public CodeBuilder ldc(LoadableConstantEntry entry) {
writeLoadConstant(BytecodeHelpers.ldcOpcode(entry), entry);
return this;
}
@Override
public CodeBuilder ldiv() {
writeBytecode(LDIV);
return this;
}
@Override
public CodeBuilder lmul() {
writeBytecode(LMUL);
return this;
}
@Override
public CodeBuilder lneg() {
writeBytecode(LNEG);
return this;
}
@Override
public CodeBuilder lor() {
writeBytecode(LOR);
return this;
}
@Override
public CodeBuilder lrem() {
writeBytecode(LREM);
return this;
}
@Override
public CodeBuilder lshl() {
writeBytecode(LSHL);
return this;
}
@Override
public CodeBuilder lshr() {
writeBytecode(LSHR);
return this;
}
@Override
public CodeBuilder lsub() {
writeBytecode(LSUB);
return this;
}
@Override
public CodeBuilder lushr() {
writeBytecode(LUSHR);
return this;
}
@Override
public CodeBuilder lxor() {
writeBytecode(LXOR);
return this;
}
@Override
public CodeBuilder monitorenter() {
writeBytecode(MONITORENTER);
return this;
}
@Override
public CodeBuilder monitorexit() {
writeBytecode(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: " + typeKind.typeName());
writeNewPrimitiveArray(atype);
return this;
}
@Override
public CodeBuilder pop() {
writeBytecode(POP);
return this;
}
@Override
public CodeBuilder pop2() {
writeBytecode(POP2);
return this;
}
@Override
public CodeBuilder sipush(int s) {
BytecodeHelpers.validateSipush(s);
writeArgumentConstant(SIPUSH, s);
return this;
}
@Override
public CodeBuilder swap() {
writeBytecode(SWAP);
return this;
}
@Override
public CodeBuilder tableswitch(int low, int high, Label defaultTarget, List cases) {
writeTableSwitch(low, high, defaultTarget, cases);
return this;
}
}