8341141: Optimize DirectCodeBuilder

Co-authored-by: Claes Redestad <redestad@openjdk.org>
Co-authored-by: Chen Liang <liach@openjdk.org>
Reviewed-by: liach, redestad
This commit is contained in:
Shaojin Wen 2024-10-09 10:01:22 +00:00
parent d636e0d314
commit 047c2d7f26
19 changed files with 884 additions and 416 deletions

View File

@ -801,7 +801,12 @@ public abstract sealed class AbstractInstruction
@Override
public void writeTo(DirectCodeBuilder writer) {
writer.writeLocalVar(op, slot);
var op = this.op;
if (op.sizeIfFixed() == 1) {
writer.writeBytecode(op);
} else {
writer.writeLocalVar(op, slot);
}
}
@Override
@ -832,7 +837,12 @@ public abstract sealed class AbstractInstruction
@Override
public void writeTo(DirectCodeBuilder writer) {
writer.writeLocalVar(op, slot);
var op = this.op;
if (op.sizeIfFixed() == 1) {
writer.writeBytecode(op);
} else {
writer.writeLocalVar(op, slot);
}
}
@Override

View File

@ -461,14 +461,13 @@ public abstract sealed class AbstractPoolEntry {
@Override
void writeTo(BufWriterImpl pool) {
pool.writeU1(TAG_UTF8);
if (rawBytes != null) {
pool.writeU2(rawLen);
pool.writeU1U2(TAG_UTF8, rawLen);
pool.writeBytes(rawBytes, offset, rawLen);
}
else {
// state == STRING and no raw bytes
pool.writeUTF(stringValue);
pool.writeUtfEntry(stringValue);
}
}
@ -502,8 +501,7 @@ public abstract sealed class AbstractPoolEntry {
}
void writeTo(BufWriterImpl pool) {
pool.writeU1(tag());
pool.writeU2(ref1.index());
pool.writeU1U2(tag(), ref1.index());
}
@Override
@ -532,9 +530,7 @@ public abstract sealed class AbstractPoolEntry {
}
void writeTo(BufWriterImpl pool) {
pool.writeU1(tag());
pool.writeU2(ref1.index());
pool.writeU2(ref2.index());
pool.writeU1U2U2(tag(), ref1.index(), ref2.index());
}
@Override
@ -864,9 +860,7 @@ public abstract sealed class AbstractPoolEntry {
}
void writeTo(BufWriterImpl pool) {
pool.writeU1(tag());
pool.writeU2(bsmIndex);
pool.writeU2(nameAndType.index());
pool.writeU1U2U2(tag(), bsmIndex, nameAndType.index());
}
@Override
@ -984,9 +978,7 @@ public abstract sealed class AbstractPoolEntry {
@Override
void writeTo(BufWriterImpl pool) {
pool.writeU1(TAG_METHOD_HANDLE);
pool.writeU1(refKind);
pool.writeU2(reference.index());
pool.writeU1U1U2(TAG_METHOD_HANDLE, refKind, reference.index());
}
@Override

View File

@ -315,8 +315,7 @@ public final class AnnotationReader {
case TypeAnnotation.TypeParameterTarget tpt -> buf.writeU1(tpt.typeParameterIndex());
case TypeAnnotation.SupertypeTarget st -> buf.writeU2(st.supertypeIndex());
case TypeAnnotation.TypeParameterBoundTarget tpbt -> {
buf.writeU1(tpbt.typeParameterIndex());
buf.writeU1(tpbt.boundIndex());
buf.writeU1U1(tpbt.typeParameterIndex(), tpbt.boundIndex());
}
case TypeAnnotation.EmptyTarget _ -> {
// nothing to write
@ -327,9 +326,7 @@ public final class AnnotationReader {
buf.writeU2(lvt.table().size());
for (var e : lvt.table()) {
int startPc = labelToBci(lr, e.startLabel(), ta);
buf.writeU2(startPc);
buf.writeU2(labelToBci(lr, e.endLabel(), ta) - startPc);
buf.writeU2(e.index());
buf.writeU2U2U2(startPc, labelToBci(lr, e.endLabel(), ta) - startPc, e.index());
}
}
case TypeAnnotation.CatchTarget ct -> buf.writeU2(ct.exceptionTableIndex());
@ -343,8 +340,7 @@ public final class AnnotationReader {
// target_path
buf.writeU1(ta.targetPath().size());
for (TypeAnnotation.TypePathComponent component : ta.targetPath()) {
buf.writeU1(component.typePathKind().tag());
buf.writeU1(component.typeArgumentIndex());
buf.writeU1U1(component.typePathKind().tag(), component.typeArgumentIndex());
}
// annotation data

View File

@ -24,14 +24,15 @@
*/
package jdk.internal.classfile.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.lang.classfile.Attribute;
import java.lang.classfile.AttributeMapper;
public class AttributeHolder {
private final List<Attribute<?>> attributes = new ArrayList<>();
private static final Attribute<?>[] EMPTY_ATTRIBUTE_ARRAY = {};
private int attributesCount = 0;
private Attribute<?>[] attributes = EMPTY_ATTRIBUTE_ARRAY;
public <A extends Attribute<A>> void withAttribute(Attribute<?> a) {
if (a == null)
@ -39,36 +40,54 @@ public class AttributeHolder {
@SuppressWarnings("unchecked")
AttributeMapper<A> am = (AttributeMapper<A>) a.attributeMapper();
if (!am.allowMultiple() && isPresent(am)) {
remove(am);
int attributesCount = this.attributesCount;
var attributes = this.attributes;
if (!am.allowMultiple()) {
// remove if
for (int i = attributesCount - 1; i >= 0; i--) {
if (attributes[i].attributeMapper() == am) {
attributesCount--;
System.arraycopy(attributes, i + 1, attributes, i, attributesCount - i);
}
}
}
attributes.add(a);
// add attribute
if (attributesCount >= attributes.length) {
int newCapacity = attributesCount + 4;
this.attributes = attributes = Arrays.copyOf(attributes, newCapacity);
}
attributes[attributesCount] = a;
this.attributesCount = attributesCount + 1;
}
public int size() {
return attributes.size();
return attributesCount;
}
public void writeTo(BufWriterImpl buf) {
Util.writeAttributes(buf, attributes);
int attributesCount = this.attributesCount;
buf.writeU2(attributesCount);
for (int i = 0; i < attributesCount; i++) {
Util.writeAttribute(buf, attributes[i]);
}
}
@SuppressWarnings("unchecked")
<A extends Attribute<A>> A get(AttributeMapper<A> am) {
for (Attribute<?> a : attributes)
for (int i = 0; i < attributesCount; i++) {
Attribute<?> a = attributes[i];
if (a.attributeMapper() == am)
return (A)a;
return (A) a;
}
return null;
}
boolean isPresent(AttributeMapper<?> am) {
for (Attribute<?> a : attributes)
if (a.attributeMapper() == am)
for (int i = 0; i < attributesCount; i++) {
if (attributes[i].attributeMapper() == am)
return true;
}
return false;
}
private void remove(AttributeMapper<?> am) {
attributes.removeIf(a -> a.attributeMapper() == am);
}
}

View File

@ -38,6 +38,7 @@ import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.ForceInline;
import static java.lang.classfile.constantpool.PoolEntry.TAG_UTF8;
import static jdk.internal.util.ModifiedUtf.putChar;
import static jdk.internal.util.ModifiedUtf.utfLen;
@ -114,6 +115,83 @@ public final class BufWriterImpl implements BufWriter {
this.offset = offset + 2;
}
@ForceInline
public void writeU1U1(int x1, int x2) {
reserveSpace(2);
byte[] elems = this.elems;
int offset = this.offset;
elems[offset ] = (byte) x1;
elems[offset + 1] = (byte) x2;
this.offset = offset + 2;
}
public void writeU1U2(int u1, int u2) {
reserveSpace(3);
byte[] elems = this.elems;
int offset = this.offset;
elems[offset ] = (byte) u1;
elems[offset + 1] = (byte) (u2 >> 8);
elems[offset + 2] = (byte) u2;
this.offset = offset + 3;
}
public void writeU1U1U1(int x1, int x2, int x3) {
reserveSpace(3);
byte[] elems = this.elems;
int offset = this.offset;
elems[offset ] = (byte) x1;
elems[offset + 1] = (byte) x2;
elems[offset + 2] = (byte) x3;
this.offset = offset + 3;
}
public void writeU1U1U2(int x1, int x2, int x3) {
reserveSpace(4);
byte[] elems = this.elems;
int offset = this.offset;
elems[offset ] = (byte) x1;
elems[offset + 1] = (byte) x2;
elems[offset + 2] = (byte) (x3 >> 8);
elems[offset + 3] = (byte) x3;
this.offset = offset + 4;
}
public void writeU1U2U2(int x1, int x2, int x3) {
reserveSpace(5);
byte[] elems = this.elems;
int offset = this.offset;
elems[offset ] = (byte) x1;
elems[offset + 1] = (byte) (x2 >> 8);
elems[offset + 2] = (byte) x2;
elems[offset + 3] = (byte) (x3 >> 8);
elems[offset + 4] = (byte) x3;
this.offset = offset + 5;
}
public void writeU2U2(int x1, int x2) {
reserveSpace(4);
byte[] elems = this.elems;
int offset = this.offset;
elems[offset ] = (byte) (x1 >> 8);
elems[offset + 1] = (byte) x1;
elems[offset + 2] = (byte) (x2 >> 8);
elems[offset + 3] = (byte) x2;
this.offset = offset + 4;
}
public void writeU2U2U2(int x1, int x2, int x3) {
reserveSpace(6);
byte[] elems = this.elems;
int offset = this.offset;
elems[offset ] = (byte) (x1 >> 8);
elems[offset + 1] = (byte) x1;
elems[offset + 2] = (byte) (x2 >> 8);
elems[offset + 3] = (byte) x2;
elems[offset + 4] = (byte) (x3 >> 8);
elems[offset + 5] = (byte) x3;
this.offset = offset + 6;
}
@Override
public void writeInt(int x) {
reserveSpace(4);
@ -162,21 +240,22 @@ public final class BufWriterImpl implements BufWriter {
}
@SuppressWarnings("deprecation")
void writeUTF(String str) {
void writeUtfEntry(String str) {
int strlen = str.length();
int countNonZeroAscii = JLA.countNonZeroAscii(str);
int utflen = utfLen(str, countNonZeroAscii);
if (utflen > 65535) {
throw new IllegalArgumentException("string too long");
}
reserveSpace(utflen + 2);
reserveSpace(utflen + 3);
int offset = this.offset;
byte[] elems = this.elems;
elems[offset ] = (byte) (utflen >> 8);
elems[offset + 1] = (byte) utflen;
offset += 2;
elems[offset ] = (byte) TAG_UTF8;
elems[offset + 1] = (byte) (utflen >> 8);
elems[offset + 2] = (byte) utflen;
offset += 3;
str.getBytes(0, countNonZeroAscii, elems, offset);
offset += countNonZeroAscii;
@ -269,13 +348,21 @@ public final class BufWriterImpl implements BufWriter {
// writeIndex methods ensure that any CP info written
// is relative to the correct constant pool
@ForceInline
@Override
public void writeIndex(PoolEntry entry) {
public int cpIndex(PoolEntry entry) {
int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index();
if (idx < 1 || idx > Character.MAX_VALUE)
throw invalidIndex(idx, entry);
writeU2(idx);
return idx;
}
@ForceInline
@Override
public void writeIndex(PoolEntry entry) {
writeU2(cpIndex(entry));
}
public void writeIndex(int bytecode, PoolEntry entry) {
writeU1U2(bytecode, cpIndex(entry));
}
static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) {

View File

@ -264,6 +264,11 @@ public class BytecodeHelpers {
};
}
public static int returnBytecode(TypeKind tk) {
int kind = Math.max(0, tk.ordinal() - 4); // BYTE, SHORT, CHAR, BOOLEAN becomes INT
return IRETURN + kind;
}
public static Opcode arrayLoadOpcode(TypeKind tk) {
return switch (tk) {
case BYTE, BOOLEAN -> Opcode.BALOAD;
@ -278,6 +283,20 @@ public class BytecodeHelpers {
};
}
public static int arrayLoadBytecode(TypeKind tk) {
return switch (tk) {
case BYTE, BOOLEAN -> BALOAD;
case SHORT -> SALOAD;
case INT -> IALOAD;
case FLOAT -> FALOAD;
case LONG -> LALOAD;
case DOUBLE -> DALOAD;
case REFERENCE -> AALOAD;
case CHAR -> CALOAD;
case VOID -> throw new IllegalArgumentException("void not an allowable array type");
};
}
public static Opcode arrayStoreOpcode(TypeKind tk) {
return switch (tk) {
case BYTE, BOOLEAN -> Opcode.BASTORE;
@ -292,6 +311,20 @@ public class BytecodeHelpers {
};
}
public static int arrayStoreBytecode(TypeKind tk) {
return switch (tk) {
case BYTE, BOOLEAN -> BASTORE;
case SHORT -> SASTORE;
case INT -> IASTORE;
case FLOAT -> FASTORE;
case LONG -> LASTORE;
case DOUBLE -> DASTORE;
case REFERENCE -> AASTORE;
case CHAR -> CASTORE;
case VOID -> throw new IllegalArgumentException("void not an allowable array type");
};
}
public static Opcode reverseBranchOpcode(Opcode op) {
return switch (op) {
case IFEQ -> Opcode.IFNE;
@ -314,6 +347,29 @@ public class BytecodeHelpers {
};
}
public static int reverseBranchOpcode(int bytecode) {
return switch (bytecode) {
case IFEQ -> IFNE;
case IFNE -> IFEQ;
case IFLT -> IFGE;
case IFGE -> IFLT;
case IFGT -> IFLE;
case IFLE -> IFGT;
case IF_ICMPEQ -> IF_ICMPNE;
case IF_ICMPNE -> IF_ICMPEQ;
case IF_ICMPLT -> IF_ICMPGE;
case IF_ICMPGE -> IF_ICMPLT;
case IF_ICMPGT -> IF_ICMPLE;
case IF_ICMPLE -> IF_ICMPGT;
case IF_ACMPEQ -> IF_ACMPNE;
case IF_ACMPNE -> IF_ACMPEQ;
case IFNULL -> IFNONNULL;
case IFNONNULL -> IFNULL;
default -> throw new IllegalArgumentException(
String.format("Wrong opcode kind specified; found %d, expected %s", bytecode, Opcode.Kind.BRANCH));
};
}
public static Opcode convertOpcode(TypeKind from, TypeKind to) {
return switch (from) {
case INT ->

View File

@ -353,7 +353,11 @@ public final class ClassReaderImpl
static <T extends PoolEntry> T checkType(PoolEntry e, int index, Class<T> cls) {
if (cls.isInstance(e)) return cls.cast(e);
throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index);
throw checkTypeError(index, cls);
}
private static ConstantPoolException checkTypeError(int index, Class<?> cls) {
return new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index);
}
@Override

View File

@ -1,5 +1,6 @@
/*
* 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
@ -30,6 +31,7 @@ import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.AccessFlag;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@ -54,9 +56,13 @@ public final class DirectClassBuilder
/** The value of default class access flags */
static final int DEFAULT_CLASS_FLAGS = ClassFile.ACC_PUBLIC;
static final Util.Writable[] EMPTY_WRITABLE_ARRAY = {};
static final ClassEntry[] EMPTY_CLASS_ENTRY_ARRAY = {};
final ClassEntry thisClassEntry;
private final List<Util.Writable> fields = new ArrayList<>();
private final List<Util.Writable> methods = new ArrayList<>();
private Util.Writable[] fields = EMPTY_WRITABLE_ARRAY;
private Util.Writable[] methods = EMPTY_WRITABLE_ARRAY;
private int fieldsCount = 0;
private int methodsCount = 0;
private ClassEntry superclassEntry;
private List<ClassEntry> interfaceEntries;
private int majorVersion;
@ -137,12 +143,20 @@ public final class DirectClassBuilder
// internal / for use by elements
ClassBuilder withField(Util.Writable field) {
fields.add(field);
if (fieldsCount >= fields.length) {
int newCapacity = fieldsCount + 8;
this.fields = Arrays.copyOf(fields, newCapacity);
}
fields[fieldsCount++] = field;
return this;
}
ClassBuilder withMethod(Util.Writable method) {
methods.add(method);
if (methodsCount >= methods.length) {
int newCapacity = methodsCount + 8;
this.methods = Arrays.copyOf(methods, newCapacity);
}
methods[methodsCount++] = method;
return this;
}
@ -184,9 +198,7 @@ public final class DirectClassBuilder
else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisClassEntry.asInternalName()))
superclass = constantPool.classEntry(ConstantDescs.CD_Object);
int interfaceEntriesSize = interfaceEntries.size();
List<ClassEntry> ies = new ArrayList<>(interfaceEntriesSize);
for (int i = 0; i < interfaceEntriesSize; i++)
ies.add(AbstractPoolEntry.maybeClone(constantPool, interfaceEntries.get(i)));
ClassEntry[] ies = interfaceEntriesSize == 0 ? EMPTY_CLASS_ENTRY_ARRAY : buildInterfaceEnties(interfaceEntriesSize);
// We maintain two writers, and then we join them at the end
int size = sizeHint == 0 ? 256 : sizeHint;
@ -195,8 +207,8 @@ public final class DirectClassBuilder
// The tail consists of fields and methods, and attributes
// This should trigger all the CP/BSM mutation
Util.writeList(tail, fields);
Util.writeList(tail, methods);
Util.writeList(tail, fields, fieldsCount);
Util.writeList(tail, methods, methodsCount);
int attributesOffset = tail.size();
attributes.writeTo(tail);
@ -211,12 +223,21 @@ public final class DirectClassBuilder
| ((minorVersion & 0xFFFFL) << 16)
| (majorVersion & 0xFFFFL));
constantPool.writeTo(head);
head.writeU2(flags);
head.writeIndex(thisClassEntry);
head.writeU2U2(flags, head.cpIndex(thisClassEntry));
head.writeIndexOrZero(superclass);
Util.writeListIndices(head, ies);
head.writeU2(interfaceEntriesSize);
for (int i = 0; i < interfaceEntriesSize; i++) {
head.writeIndex(ies[i]);
}
// Join head and tail into an exact-size buffer
return BufWriterImpl.join(head, tail);
}
private ClassEntry[] buildInterfaceEnties(int interfaceEntriesSize) {
var ies = new ClassEntry[interfaceEntriesSize];
for (int i = 0; i < interfaceEntriesSize; i++)
ies[i] = AbstractPoolEntry.maybeClone(constantPool, interfaceEntries.get(i));
return ies;
}
}

View File

@ -148,9 +148,7 @@ public final class DirectMethodBuilder
@Override
public void writeTo(BufWriterImpl buf) {
buf.writeU2(flags);
buf.writeIndex(name);
buf.writeIndex(desc);
buf.writeU2U2U2(flags, buf.cpIndex(name), buf.cpIndex(desc));
attributes.writeTo(buf);
}
}

View File

@ -113,6 +113,7 @@ public final class EntryMap {
throw new IllegalArgumentException("hash must be nonzero");
int ptr = (hash & mask1) << 1;
var data = this.data;
int k = data[ptr];
if (k == 0) {
data[ptr] = hash;

View File

@ -24,8 +24,6 @@
*/
package jdk.internal.classfile.impl;
import java.util.Objects;
import java.lang.classfile.Label;
import java.lang.classfile.instruction.LabelTarget;
@ -52,7 +50,7 @@ public final class LabelImpl
private int bci;
public LabelImpl(LabelContext labelContext, int bci) {
this.labelContext = Objects.requireNonNull(labelContext);
this.labelContext = labelContext;
this.bci = bci;
}

View File

@ -34,7 +34,6 @@ import java.lang.classfile.ClassReader;
import java.lang.classfile.BootstrapMethodEntry;
import java.lang.classfile.attribute.BootstrapMethodsAttribute;
import java.lang.classfile.constantpool.*;
import java.util.Objects;
import jdk.internal.constant.ConstantUtils;
@ -87,20 +86,27 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
@Override
public PoolEntry entryByIndex(int index) {
if (index <= 0 || index >= size()) {
throw new ConstantPoolException("Bad CP index: " + index);
throw badCP(index);
}
PoolEntry pe = (index < parentSize)
? parent.entryByIndex(index)
: myEntries[index - parentSize];
if (pe == null) {
throw new ConstantPoolException("Unusable CP index: " + index);
throw unusableCP(index);
}
return pe;
}
private static ConstantPoolException badCP(int index) {
return new ConstantPoolException("Bad CP index: " + index);
}
private static ConstantPoolException unusableCP(int index) {
return new ConstantPoolException("Unusable CP index: " + index);
}
@Override
public <T extends PoolEntry> T entryByIndex(int index, Class<T> cls) {
Objects.requireNonNull(cls);
return ClassReaderImpl.checkType(entryByIndex(index), index, cls);
}
@ -165,8 +171,10 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
}
private EntryMap map() {
int parentSize = this.parentSize;
var map = this.map;
if (map == null) {
map = new EntryMap(Math.max(size, 1024), .75f);
this.map = map = new EntryMap(Math.max(size, 1024), .75f);
// Doing a full scan here yields fall-off-the-cliff performance results,
// especially if we only need a few entries that are already
@ -203,8 +211,10 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
}
private EntryMap bsmMap() {
int bsmSize = this.bsmSize;
var bsmMap = this.bsmMap;
if (bsmMap == null) {
bsmMap = new EntryMap(Math.max(bsmSize, 16), .75f);
this.bsmMap = bsmMap = new EntryMap(Math.max(bsmSize, 16), .75f);
for (int i=0; i<parentBsmSize; i++) {
BootstrapMethodEntryImpl bsm = parent.bootstrapMethodEntry(i);
bsmMap.put(bsm.hash, bsm.index);

View File

@ -48,7 +48,7 @@ public class StackMapDecoder {
private static final int
SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
SAME_EXTENDED = 251;
private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = new StackMapFrameInfo[0];
private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {};
private final ClassReader classReader;
private final int pos;

View File

@ -163,7 +163,7 @@ public final class StackMapGenerator {
private static final int FLAG_THIS_UNINIT = 0x01;
private static final int FRAME_DEFAULT_CAPACITY = 10;
private static final int T_BOOLEAN = 4, T_LONG = 11;
private static final Frame[] EMPTY_FRAME_ARRAY = new Frame[0];
private static final Frame[] EMPTY_FRAME_ARRAY = {};
private static final int ITEM_TOP = 0,
ITEM_INTEGER = 1,
@ -1219,7 +1219,9 @@ public final class StackMapGenerator {
return Arrays.equals(l1, 0, commonSize, l2, 0, commonSize);
}
void writeTo(BufWriter out, Frame prevFrame, ConstantPoolBuilder cp) {
void writeTo(BufWriterImpl out, Frame prevFrame, ConstantPoolBuilder cp) {
int localsSize = this.localsSize;
int stackSize = this.stackSize;
int offsetDelta = offset - prevFrame.offset - 1;
if (stackSize == 0) {
int commonLocalsSize = localsSize > prevFrame.localsSize ? prevFrame.localsSize : localsSize;
@ -1228,8 +1230,7 @@ public final class StackMapGenerator {
if (diffLocalsSize == 0 && offsetDelta < 64) { //same frame
out.writeU1(offsetDelta);
} else { //chop, same extended or append frame
out.writeU1(251 + diffLocalsSize);
out.writeU2(offsetDelta);
out.writeU1U2(251 + diffLocalsSize, offsetDelta);
for (int i=commonLocalsSize; i<localsSize; i++) locals[i].writeTo(out, cp);
}
return;
@ -1238,15 +1239,13 @@ public final class StackMapGenerator {
if (offsetDelta < 64) { //same locals 1 stack item frame
out.writeU1(64 + offsetDelta);
} else { //same locals 1 stack item extended frame
out.writeU1(247);
out.writeU2(offsetDelta);
out.writeU1U2(247, offsetDelta);
}
stack[0].writeTo(out, cp);
return;
}
//full frame
out.writeU1(255);
out.writeU2(offsetDelta);
out.writeU1U2(255, offsetDelta);
out.writeU2(localsSize);
for (int i=0; i<localsSize; i++) locals[i].writeTo(out, cp);
out.writeU2(stackSize);

View File

@ -232,7 +232,7 @@ public class Util {
}
@SuppressWarnings("unchecked")
private static <T extends Attribute<T>> void writeAttribute(BufWriterImpl writer, Attribute<?> attr) {
public static <T extends Attribute<T>> void writeAttribute(BufWriterImpl writer, Attribute<?> attr) {
if (attr instanceof CustomAttribute<?> ca) {
var mapper = (AttributeMapper<T>) ca.attributeMapper();
mapper.writeAttribute(writer, (T) ca);
@ -252,11 +252,10 @@ public class Util {
}
@ForceInline
static void writeList(BufWriterImpl buf, List<Writable> list) {
int size = list.size();
static void writeList(BufWriterImpl buf, Writable[] array, int size) {
buf.writeU2(size);
for (int i = 0; i < size; i++) {
list.get(i).writeTo(buf);
array[i].writeTo(buf);
}
}
@ -401,6 +400,7 @@ public class Util {
0x54fbc001, 0xb9f78001, 0x2ef34001, 0xb3ef0001, 0x48eac001, 0xede68001, 0xa2e24001,
0x67de0001, 0xcfbc0001, 0x379a0001, 0x9f780001, 0x07560001, 0x6f340001, 0xd7120001,
0x3ef00001, 0x7de00001, 0xbcd00001, 0xfbc00001, 0x3ab00001, 0x79a00001, 0xb8900001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static int powersIndex(int digit, int index) {
@ -411,6 +411,6 @@ public class Util {
// digit: 0 - 7
// index: 0 - SIGNIFICANT_OCTAL_DIGITS - 1
private static int powerOctal(int digit, int index) {
return digit == 0 ? 1 : powers[powersIndex(digit, index)];
return digit == 0 ? 1 : powers[powersIndex(digit, index) & 0x3F]; // & 0x3F eliminates bound check
}
}

View File

@ -287,15 +287,31 @@ public final class MethodTypeDescImpl implements MethodTypeDesc {
if (desc != null)
return desc;
int len = 2 + returnType.descriptorString().length();
for (ClassDesc argType : argTypes) {
len += argType.descriptorString().length();
return buildDescriptorString();
}
private String buildDescriptorString() {
var returnType = this.returnType;
var returnTypeDesc = returnType.descriptorString();
var argTypes = this.argTypes;
String desc;
if (argTypes.length == 0) {
// getter
desc = "()".concat(returnTypeDesc);
} else if (argTypes.length == 1 && returnType == ConstantDescs.CD_void) {
// setter
desc = ConstantUtils.concat("(", argTypes[0].descriptorString(), ")V");
} else {
int len = 2 + returnTypeDesc.length();
for (ClassDesc argType : argTypes) {
len += argType.descriptorString().length();
}
StringBuilder sb = new StringBuilder(len).append('(');
for (ClassDesc argType : argTypes) {
sb.append(argType.descriptorString());
}
desc = sb.append(')').append(returnTypeDesc).toString();
}
StringBuilder sb = new StringBuilder(len).append('(');
for (ClassDesc argType : argTypes) {
sb.append(argType.descriptorString());
}
desc = sb.append(')').append(returnType.descriptorString()).toString();
cachedDescriptorString = desc;
return desc;
}

View File

@ -120,7 +120,7 @@ class UtilTest {
// Ensures the initialization statement of the powers array is filling in the right values
@Test
void testPowersArray() {
int[] powers = new int[7 * UtilAccess.significantOctalDigits()];
int[] powers = new int[64];
for (int i = 1, k = 31; i <= 7; i++, k *= 31) {
int t = powers[UtilAccess.powersIndex(i, 0)] = k;

View File

@ -141,9 +141,9 @@ public class Write {
cb.withVersion(52, 0);
cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
.withMethod(INIT_NAME, MTD_void, 0, mb -> mb
.withCode(codeb -> codeb.loadLocal(REFERENCE, 0)
.invoke(INVOKESPECIAL, CD_Object, INIT_NAME, MTD_void, false)
.return_(VOID)
.withCode(codeb -> codeb.aload(0)
.invokespecial(CD_Object, INIT_NAME, MTD_void)
.return_()
)
);
for (int xi = 0; xi < 40; ++xi) {
@ -180,54 +180,6 @@ public class Write {
return bytes;
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public byte[] jdkTreePrimitive() {
byte[] bytes = ClassFile.of().build(CD_MyClass, cb -> {
cb.withFlags(AccessFlag.PUBLIC);
cb.withVersion(52, 0);
cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java"))))
.withMethod(INIT_NAME, MTD_void, 0,
mb -> mb.withCode(codeb -> codeb.loadLocal(REFERENCE, 0)
.invokespecial(CD_Object, INIT_NAME, MTD_void, false)
.return_()
)
);
for (int xi = 0; xi < 40; ++xi) {
cb.withMethod("main" + ((xi == 0) ? "" : "" + xi), MTD_void_StringArray,
ACC_PUBLIC | ACC_STATIC,
mb -> mb.withCode(c0 -> {
java.lang.classfile.Label loopTop = c0.newLabel();
java.lang.classfile.Label loopEnd = c0.newLabel();
int vFac = 1;
int vI = 2;
c0.iconst_1() // 0
.istore(1) // 1
.iconst_1() // 2
.istore(2) // 3
.labelBinding(loopTop)
.iload(2) // 4
.bipush(10) // 5
.if_icmpge(loopEnd) // 6
.iload(1) // 7
.iload(2) // 8
.imul() // 9
.istore(1) // 10
.iinc(2, 1) // 11
.goto_(loopTop) // 12
.labelBinding(loopEnd)
.getstatic(CD_System, "out", CD_PrintStream) // 13
.iload(1)
.invokevirtual(CD_PrintStream, "println", MTD_void_int) // 15
.return_();
}));
}
});
if (writeClassBc) writeClass(bytes, checkFileBc);
return bytes;
}
private void writeClass(byte[] bytes, String fn) {
try {
FileOutputStream out = new FileOutputStream(fn);