From 047c2d7f2676b8c3c8b5645134fb5c00c540e43f Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 9 Oct 2024 10:01:22 +0000 Subject: [PATCH] 8341141: Optimize DirectCodeBuilder Co-authored-by: Claes Redestad Co-authored-by: Chen Liang Reviewed-by: liach, redestad --- .../classfile/impl/AbstractInstruction.java | 14 +- .../classfile/impl/AbstractPoolEntry.java | 20 +- .../classfile/impl/AnnotationReader.java | 10 +- .../classfile/impl/AttributeHolder.java | 51 +- .../classfile/impl/BufWriterImpl.java | 105 ++- .../classfile/impl/BytecodeHelpers.java | 56 ++ .../classfile/impl/ClassReaderImpl.java | 6 +- .../classfile/impl/DirectClassBuilder.java | 45 +- .../classfile/impl/DirectCodeBuilder.java | 847 ++++++++++++------ .../classfile/impl/DirectMethodBuilder.java | 4 +- .../jdk/internal/classfile/impl/EntryMap.java | 1 + .../internal/classfile/impl/LabelImpl.java | 4 +- .../classfile/impl/SplitConstantPool.java | 22 +- .../classfile/impl/StackMapDecoder.java | 2 +- .../classfile/impl/StackMapGenerator.java | 15 +- .../jdk/internal/classfile/impl/Util.java | 10 +- .../internal/constant/MethodTypeDescImpl.java | 32 +- test/jdk/jdk/classfile/UtilTest.java | 2 +- .../openjdk/bench/jdk/classfile/Write.java | 54 +- 19 files changed, 884 insertions(+), 416 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index b5d6ea24059..6b77f6ff1ad 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -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 diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 1d8d298857a..4a1803598ff 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -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 diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java index e33012ef183..02f6167d436 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java @@ -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 diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java index fb9ecc98902..6060170a8d1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java @@ -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> attributes = new ArrayList<>(); + private static final Attribute[] EMPTY_ATTRIBUTE_ARRAY = {}; + private int attributesCount = 0; + private Attribute[] attributes = EMPTY_ATTRIBUTE_ARRAY; public > void withAttribute(Attribute a) { if (a == null) @@ -39,36 +40,54 @@ public class AttributeHolder { @SuppressWarnings("unchecked") AttributeMapper am = (AttributeMapper) 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 get(AttributeMapper 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); - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index 1a7c9a36c44..cf5b98dafb1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -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) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java index 4f4e1021712..bca80c1ed4b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java @@ -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 -> diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java index 2ef39504e9b..0f183ad427f 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java @@ -353,7 +353,11 @@ public final class ClassReaderImpl static T checkType(PoolEntry e, int index, Class 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 diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index 5f02ae708ea..b599f2b61aa 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -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 fields = new ArrayList<>(); - private final List 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 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 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; + } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index b986efde1a5..72c37ade9ac 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -25,6 +25,8 @@ */ package jdk.internal.classfile.impl; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.classfile.Attribute; import java.lang.classfile.Attributes; import java.lang.classfile.ClassFile; @@ -52,8 +54,8 @@ import java.lang.classfile.instruction.ExceptionCatch; import java.lang.classfile.instruction.LocalVariable; import java.lang.classfile.instruction.LocalVariableType; import java.lang.classfile.instruction.SwitchCase; -import java.lang.constant.ConstantDesc; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; @@ -62,18 +64,27 @@ import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; -import static java.lang.classfile.Opcode.*; - import static jdk.internal.classfile.impl.BytecodeHelpers.*; +import static jdk.internal.classfile.impl.RawBytecodeHelper.*; public final class DirectCodeBuilder extends AbstractDirectBuilder implements TerminalCodeBuilder { - private final List characterRanges = new ArrayList<>(); + 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 final List localVariables = new ArrayList<>(); - private final List localVariableTypes = new ArrayList<>(); - private final boolean transformFwdJumps, transformBackJumps; + 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; @@ -83,7 +94,8 @@ public final class DirectCodeBuilder private DedupLineNumberTableAttribute lineNumberWriter; private int topLocal; - List deferredLabels; + private DeferredLabel[] deferredLabels = EMPTY_DEFERRED_LABEL_ARRAY; + private int deferredLabelsCount = 0; /* Locals management lazily computed maxLocal = -1 @@ -117,12 +129,12 @@ public final class DirectCodeBuilder SplitConstantPool constantPool, ClassFileImpl context, CodeModel original, - boolean transformFwdJumps) { + boolean transformDeferredJumps) { super(constantPool, context); setOriginal(original); this.methodInfo = methodInfo; - this.transformFwdJumps = transformFwdJumps; - this.transformBackJumps = context.fixShortJumps(); + 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); @@ -208,9 +220,7 @@ public final class DirectCodeBuilder throw new IllegalArgumentException("Unbound label in exception handler"); } } else { - buf.writeU2(startPc); - buf.writeU2(endPc); - buf.writeU2(handlerPc); + buf.writeU2U2U2(startPc, endPc, handlerPc); buf.writeIndexOrZero(h.catchTypeEntry()); handlersSize++; } @@ -227,15 +237,16 @@ public final class DirectCodeBuilder processDeferredLabels(); if (context.passDebugElements()) { - if (!characterRanges.isEmpty()) { + if (characterRangesCount > 0) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) { @Override public void writeBody(BufWriterImpl b) { int pos = b.size(); - int crSize = characterRanges.size(); + int crSize = characterRangesCount; b.writeU2(crSize); - for (CharacterRange cr : characterRanges) { + 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) { @@ -245,28 +256,28 @@ public final class DirectCodeBuilder throw new IllegalArgumentException("Unbound label in character range"); } } else { - b.writeU2(start); - b.writeU2(end - 1); + b.writeU2U2(start, end - 1); b.writeInt(cr.characterRangeStart()); b.writeInt(cr.characterRangeEnd()); b.writeU2(cr.flags()); } } - if (crSize < characterRanges.size()) + if (crSize < characterRangesCount) b.patchU2(pos, crSize); } }; attributes.withAttribute(a); } - if (!localVariables.isEmpty()) { + if (localVariablesCount > 0) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTable()) { @Override public void writeBody(BufWriterImpl b) { int pos = b.size(); - int lvSize = localVariables.size(); + int lvSize = localVariablesCount; b.writeU2(lvSize); - for (LocalVariable l : localVariables) { + for (int i = 0; i < localVariablesCount; i++) { + LocalVariable l = localVariables[i]; if (!Util.writeLocalVariable(b, l)) { if (context.dropDeadLabels()) { lvSize--; @@ -275,21 +286,22 @@ public final class DirectCodeBuilder } } } - if (lvSize < localVariables.size()) + if (lvSize < localVariablesCount) b.patchU2(pos, lvSize); } }; attributes.withAttribute(a); } - if (!localVariableTypes.isEmpty()) { + if (localVariableTypesCount > 0) { 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) { + 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--; @@ -298,7 +310,7 @@ public final class DirectCodeBuilder } } } - if (lvtSize < localVariableTypes.size()) + if (lvtSize < localVariableTypesCount) b.patchU2(pos, lvtSize); } }; @@ -315,22 +327,20 @@ public final class DirectCodeBuilder private void writeCounters(boolean codeMatch, BufWriterImpl buf) { if (codeMatch) { var originalAttribute = (CodeImpl) original; - buf.writeU2(originalAttribute.maxStack()); - buf.writeU2(originalAttribute.maxLocals()); + buf.writeU2U2(originalAttribute.maxStack(), originalAttribute.maxLocals()); } else { StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); - buf.writeU2(cntr.maxStack()); - buf.writeU2(cntr.maxLocals()); + 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 - StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf); - attributes.withAttribute(gen.stackMapTableAttribute()); - buf.writeU2(gen.maxStack()); - buf.writeU2(gen.maxLocals()); + 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) { @@ -352,20 +362,22 @@ public final class DirectCodeBuilder @Override public void writeBody(BufWriterImpl buf) { - buf.setLabelContext(DirectCodeBuilder.this); + 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, - methodInfo.methodName().stringValue(), - methodInfo.methodTypeSymbol().displayDescriptor())); + dcb.methodInfo.methodName().stringValue(), + dcb.methodInfo.methodTypeSymbol().displayDescriptor())); } - if (codeAndExceptionsMatch(codeLength)) { + var context = dcb.context; + if (dcb.original != null && codeAndExceptionsMatch(codeLength)) { if (context.stackMapsWhenRequired()) { - attributes.withAttribute(original.findAttribute(Attributes.stackMapTable()).orElse(null)); + dcb.attributes.withAttribute(dcb.original.findAttribute(Attributes.stackMapTable()).orElse(null)); writeCounters(true, buf); } else if (context.generateStackMaps()) { generateStackMaps(buf); @@ -383,9 +395,9 @@ public final class DirectCodeBuilder } buf.writeInt(codeLength); - buf.writeBytes(bytecodesBufWriter); - writeExceptionHandlers(buf); - attributes.writeTo(buf); + buf.writeBytes(dcb.bytecodesBufWriter); + dcb.writeExceptionHandlers(buf); + dcb.attributes.writeTo(buf); buf.setLabelContext(null); } }; @@ -405,8 +417,7 @@ public final class DirectCodeBuilder private void push() { //subsequent identical line numbers are skipped if (lastPc >= 0 && lastLine != writtenLine) { - buf.writeU2(lastPc); - buf.writeU2(lastLine); + buf.writeU2U2(lastPc, lastLine); writtenLine = lastLine; } } @@ -456,32 +467,16 @@ public final class DirectCodeBuilder 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 = bytecodesBufWriter.skip(nBytes); - 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) { - if ((short)branchOffset != branchOffset) throw new LabelOverflowException(); - bytecodesBufWriter.patchU2(dl.labelPc, branchOffset); - } else { - assert dl.size == 4; - bytecodesBufWriter.patchInt(dl.labelPc, branchOffset); - } + 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); } } } @@ -489,72 +484,140 @@ public final class DirectCodeBuilder // 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.writeU2(opcode.bytecode()); + bytecodesBufWriter.writeU2U2(opcode.bytecode(), slot); } else { - bytecodesBufWriter.writeU1(opcode.bytecode()); + bytecodesBufWriter.writeU1U1(opcode.bytecode(), slot); } } - 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); + // 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.writeU1(RawBytecodeHelper.WIDE); - } - bytecodesBufWriter.writeU1(RawBytecodeHelper.IINC); - if (wide) { - bytecodesBufWriter.writeU2(slot); - bytecodesBufWriter.writeU2(val); + bytecodesBufWriter.writeU2U2U2((WIDE << 8) | IINC, slot, val); } else { - bytecodesBufWriter.writeU1(slot); - bytecodesBufWriter.writeU1(val); + 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); - //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); - } + + // algebraic union of jump | (instructionPc, target), distinguished by null == target. + int jumpOrInstructionPc; + Label nullOrTarget; + if (targetBci == -1) { + jumpOrInstructionPc = instructionPc; + nullOrTarget = target; } else { - writeBytecode(op); - writeLabelOffset(op.sizeIfFixed() == 3 ? 2 : 4, instructionPc, target); + 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(); - writeBytecode(LOOKUPSWITCH); + bytecodesBufWriter.writeU1(LOOKUPSWITCH); int pad = 4 - (curPc() % 4); if (pad != 4) bytecodesBufWriter.skip(pad); // padding content can be anything - writeLabelOffset(4, instructionPc, defaultTarget); + writeLongLabelOffset(instructionPc, defaultTarget); bytecodesBufWriter.writeInt(cases.size()); cases = new ArrayList<>(cases); cases.sort(new Comparator<>() { @@ -565,17 +628,18 @@ public final class DirectCodeBuilder }); for (var c : cases) { bytecodesBufWriter.writeInt(c.caseValue()); - writeLabelOffset(4, instructionPc, c.target()); + var target = c.target(); + writeLongLabelOffset(instructionPc, target); } } public void writeTableSwitch(int low, int high, Label defaultTarget, List cases) { int instructionPc = curPc(); - writeBytecode(TABLESWITCH); + bytecodesBufWriter.writeU1(TABLESWITCH); int pad = 4 - (curPc() % 4); if (pad != 4) bytecodesBufWriter.skip(pad); // padding content can be anything - writeLabelOffset(4, instructionPc, defaultTarget); + writeLongLabelOffset(instructionPc, defaultTarget); bytecodesBufWriter.writeInt(low); bytecodesBufWriter.writeInt(high); var caseMap = new HashMap(cases.size()); @@ -583,85 +647,74 @@ public final class DirectCodeBuilder caseMap.put(c.caseValue(), c.target()); } for (long l = low; l<=high; l++) { - writeLabelOffset(4, instructionPc, caseMap.getOrDefault((int)l, defaultTarget)); + var target = caseMap.getOrDefault((int)l, defaultTarget); + writeLongLabelOffset(instructionPc, target); } } public void writeFieldAccess(Opcode opcode, FieldRefEntry ref) { - writeBytecode(opcode); - bytecodesBufWriter.writeIndex(ref); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); } public void writeInvokeNormal(Opcode opcode, MemberRefEntry ref) { - writeBytecode(opcode); - bytecodesBufWriter.writeIndex(ref); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); } public void writeInvokeInterface(Opcode opcode, InterfaceMethodRefEntry ref, int count) { - writeBytecode(opcode); - bytecodesBufWriter.writeIndex(ref); - bytecodesBufWriter.writeU1(count); - bytecodesBufWriter.writeU1(0); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); + bytecodesBufWriter.writeU1U1(count, 0); } public void writeInvokeDynamic(InvokeDynamicEntry ref) { - writeBytecode(INVOKEDYNAMIC); - bytecodesBufWriter.writeIndex(ref); + bytecodesBufWriter.writeIndex(INVOKEDYNAMIC, ref); bytecodesBufWriter.writeU2(0); } public void writeNewObject(ClassEntry type) { - writeBytecode(NEW); - bytecodesBufWriter.writeIndex(type); + bytecodesBufWriter.writeIndex(NEW, type); } public void writeNewPrimitiveArray(int newArrayCode) { - writeBytecode(NEWARRAY); - bytecodesBufWriter.writeU1(newArrayCode); + bytecodesBufWriter.writeU1U1(NEWARRAY, newArrayCode); } public void writeNewReferenceArray(ClassEntry type) { - writeBytecode(ANEWARRAY); - bytecodesBufWriter.writeIndex(type); + bytecodesBufWriter.writeIndex(ANEWARRAY, type); } public void writeNewMultidimensionalArray(int dimensions, ClassEntry type) { - writeBytecode(MULTIANEWARRAY); - bytecodesBufWriter.writeIndex(type); + bytecodesBufWriter.writeIndex(MULTIANEWARRAY, type); bytecodesBufWriter.writeU1(dimensions); } public void writeTypeCheck(Opcode opcode, ClassEntry type) { - writeBytecode(opcode); - bytecodesBufWriter.writeIndex(type); + bytecodesBufWriter.writeIndex(opcode.bytecode(), type); } public void writeArgumentConstant(Opcode opcode, int value) { - writeBytecode(opcode); if (opcode.sizeIfFixed() == 3) { - bytecodesBufWriter.writeU2(value); + bytecodesBufWriter.writeU1U2(opcode.bytecode(), value); } else { - bytecodesBufWriter.writeU1(value); + bytecodesBufWriter.writeU1U1(opcode.bytecode(), value); } } public void writeLoadConstant(Opcode opcode, LoadableConstantEntry value) { // Make sure Long and Double have LDC2_W and - // rewrite to _W if index is > 256 + // 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; + opcode = Opcode.LDC2_W; } else if (index >= 256) - op = LDC_W; + opcode = Opcode.LDC_W; - writeBytecode(op); - if (op.sizeIfFixed() == 3) { - bytecodesBufWriter.writeU2(index); + assert !opcode.isWide(); + if (opcode.sizeIfFixed() == 3) { + bytecodesBufWriter.writeU1U2(opcode.bytecode(), index); } else { - bytecodesBufWriter.writeU1(index); + bytecodesBufWriter.writeU1U1(opcode.bytecode(), index); } } @@ -677,7 +730,11 @@ public final class DirectCodeBuilder if (context == this) { return lab.getBCI(); } - else if (context == mruParent) { + 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) { @@ -717,14 +774,18 @@ public final class DirectCodeBuilder @Override public void setLabelTarget(Label label, int bci) { LabelImpl lab = (LabelImpl) label; - LabelContext context = lab.labelContext(); - - if (context == this) { + 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); } - else if (context == mruParent) { + } + + 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) { @@ -739,7 +800,7 @@ public final class DirectCodeBuilder mruParent = parent; mruParentTable = table; - mruParentTable[lab.getBCI()] = bci + 1; + table[lab.getBCI()] = bci + 1; } else if (context instanceof BufferedCodeBuilder) { // Hijack the label @@ -751,7 +812,19 @@ public final class DirectCodeBuilder } public void addCharacterRange(CharacterRange element) { - characterRanges.add(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) { @@ -763,11 +836,19 @@ public final class DirectCodeBuilder } public void addLocalVariable(LocalVariable element) { - localVariables.add(element); + if (localVariablesCount >= localVariables.length) { + int newCapacity = localVariablesCount + 8; + this.localVariables = Arrays.copyOf(localVariables, newCapacity); + } + localVariables[localVariablesCount++] = element; } public void addLocalVariableType(LocalVariableType element) { - localVariableTypes.add(element); + if (localVariableTypesCount >= localVariableTypes.length) { + int newCapacity = localVariableTypesCount + 8; + this.localVariableTypes = Arrays.copyOf(localVariableTypes, newCapacity); + } + localVariableTypes[localVariableTypesCount++] = element; } @Override @@ -788,27 +869,53 @@ public final class DirectCodeBuilder // 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) { - writeBytecode(BytecodeHelpers.returnOpcode(tk)); + bytecodesBufWriter.writeU1(returnBytecode(tk)); return this; } @Override public CodeBuilder storeLocal(TypeKind tk, int slot) { - writeLocalVar(BytecodeHelpers.storeOpcode(tk, slot), 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) { - writeLocalVar(BytecodeHelpers.loadOpcode(tk, slot), slot); - return this; + 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 == INVOKEINTERFACE) { + if (opcode == Opcode.INVOKEINTERFACE) { int slots = Util.parameterSlots(Util.methodTypeSymbol(ref.type())) + 1; writeInvokeInterface(opcode, (InterfaceMethodRefEntry) ref, slots); } else { @@ -817,21 +924,45 @@ public final class DirectCodeBuilder 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) { - writeFieldAccess(opcode, ref); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); return this; } @Override public CodeBuilder arrayLoad(TypeKind tk) { - writeBytecode(BytecodeHelpers.arrayLoadOpcode(tk)); + bytecodesBufWriter.writeU1(BytecodeHelpers.arrayLoadBytecode(tk)); return this; } @Override public CodeBuilder arrayStore(TypeKind tk) { - writeBytecode(BytecodeHelpers.arrayStoreOpcode(tk)); + bytecodesBufWriter.writeU1(BytecodeHelpers.arrayStoreBytecode(tk)); return this; } @@ -843,19 +974,23 @@ public final class DirectCodeBuilder @Override public CodeBuilder nop() { - writeBytecode(NOP); + bytecodesBufWriter.writeU1(NOP); return this; } @Override public CodeBuilder aconst_null() { - writeBytecode(ACONST_NULL); + bytecodesBufWriter.writeU1(ACONST_NULL); return this; } @Override public CodeBuilder aload(int slot) { - writeLocalVar(BytecodeHelpers.aload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ALOAD_0 + slot); + } else { + writeLocalVar(ALOAD, slot); + } return this; } @@ -867,350 +1002,496 @@ public final class DirectCodeBuilder @Override public CodeBuilder arraylength() { - writeBytecode(ARRAYLENGTH); + bytecodesBufWriter.writeU1(ARRAYLENGTH); + return this; + } + + @Override + public CodeBuilder areturn() { + bytecodesBufWriter.writeU1(ARETURN); return this; } @Override public CodeBuilder astore(int slot) { - writeLocalVar(BytecodeHelpers.astore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ASTORE_0 + slot); + } else { + writeLocalVar(ASTORE, slot); + } return this; } @Override public CodeBuilder athrow() { - writeBytecode(ATHROW); + bytecodesBufWriter.writeU1(ATHROW); return this; } @Override public CodeBuilder bipush(int b) { BytecodeHelpers.validateBipush(b); - writeArgumentConstant(BIPUSH, b); + bytecodesBufWriter.writeU1U1(BIPUSH, b); return this; } @Override public CodeBuilder checkcast(ClassEntry type) { - writeTypeCheck(CHECKCAST, type); + bytecodesBufWriter.writeIndex(CHECKCAST, type); return this; } @Override public CodeBuilder d2f() { - writeBytecode(D2F); + bytecodesBufWriter.writeU1(D2F); return this; } @Override public CodeBuilder d2i() { - writeBytecode(D2I); + bytecodesBufWriter.writeU1(D2I); return this; } @Override public CodeBuilder d2l() { - writeBytecode(D2L); + bytecodesBufWriter.writeU1(D2L); return this; } @Override public CodeBuilder dadd() { - writeBytecode(DADD); + bytecodesBufWriter.writeU1(DADD); return this; } @Override public CodeBuilder dcmpg() { - writeBytecode(DCMPG); + bytecodesBufWriter.writeU1(DCMPG); return this; } @Override public CodeBuilder dcmpl() { - writeBytecode(DCMPL); + bytecodesBufWriter.writeU1(DCMPL); return this; } @Override public CodeBuilder dconst_0() { - writeBytecode(DCONST_0); + bytecodesBufWriter.writeU1(DCONST_0); return this; } @Override public CodeBuilder dconst_1() { - writeBytecode(DCONST_1); + bytecodesBufWriter.writeU1(DCONST_1); return this; } @Override public CodeBuilder ddiv() { - writeBytecode(DDIV); + bytecodesBufWriter.writeU1(DDIV); return this; } @Override public CodeBuilder dload(int slot) { - writeLocalVar(BytecodeHelpers.dload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(DLOAD_0 + slot); + } else { + writeLocalVar(DLOAD, slot); + } return this; } @Override public CodeBuilder dmul() { - writeBytecode(DMUL); + bytecodesBufWriter.writeU1(DMUL); return this; } @Override public CodeBuilder dneg() { - writeBytecode(DNEG); + bytecodesBufWriter.writeU1(DNEG); return this; } @Override public CodeBuilder drem() { - writeBytecode(DREM); + bytecodesBufWriter.writeU1(DREM); + return this; + } + + @Override + public CodeBuilder dreturn() { + bytecodesBufWriter.writeU1(DRETURN); return this; } @Override public CodeBuilder dstore(int slot) { - writeLocalVar(BytecodeHelpers.dstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(DSTORE_0 + slot); + } else { + writeLocalVar(DSTORE, slot); + } return this; } @Override public CodeBuilder dsub() { - writeBytecode(DSUB); + bytecodesBufWriter.writeU1(DSUB); return this; } @Override public CodeBuilder dup() { - writeBytecode(DUP); + bytecodesBufWriter.writeU1(DUP); return this; } @Override public CodeBuilder dup2() { - writeBytecode(DUP2); + bytecodesBufWriter.writeU1(DUP2); return this; } @Override public CodeBuilder dup2_x1() { - writeBytecode(DUP2_X1); + bytecodesBufWriter.writeU1(DUP2_X1); return this; } @Override public CodeBuilder dup2_x2() { - writeBytecode(DUP2_X2); + bytecodesBufWriter.writeU1(DUP2_X2); return this; } @Override public CodeBuilder dup_x1() { - writeBytecode(DUP_X1); + bytecodesBufWriter.writeU1(DUP_X1); return this; } @Override public CodeBuilder dup_x2() { - writeBytecode(DUP_X2); + bytecodesBufWriter.writeU1(DUP_X2); return this; } @Override public CodeBuilder f2d() { - writeBytecode(F2D); + bytecodesBufWriter.writeU1(F2D); return this; } @Override public CodeBuilder f2i() { - writeBytecode(F2I); + bytecodesBufWriter.writeU1(F2I); return this; } @Override public CodeBuilder f2l() { - writeBytecode(F2L); + bytecodesBufWriter.writeU1(F2L); return this; } @Override public CodeBuilder fadd() { - writeBytecode(FADD); + bytecodesBufWriter.writeU1(FADD); return this; } @Override public CodeBuilder fcmpg() { - writeBytecode(FCMPG); + bytecodesBufWriter.writeU1(FCMPG); return this; } @Override public CodeBuilder fcmpl() { - writeBytecode(FCMPL); + bytecodesBufWriter.writeU1(FCMPL); return this; } @Override public CodeBuilder fconst_0() { - writeBytecode(FCONST_0); + bytecodesBufWriter.writeU1(FCONST_0); return this; } @Override public CodeBuilder fconst_1() { - writeBytecode(FCONST_1); + bytecodesBufWriter.writeU1(FCONST_1); return this; } @Override public CodeBuilder fconst_2() { - writeBytecode(FCONST_2); + bytecodesBufWriter.writeU1(FCONST_2); return this; } @Override public CodeBuilder fdiv() { - writeBytecode(FDIV); + bytecodesBufWriter.writeU1(FDIV); return this; } @Override public CodeBuilder fload(int slot) { - writeLocalVar(BytecodeHelpers.fload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(FLOAD_0 + slot); + } else { + writeLocalVar(FLOAD, slot); + } return this; } @Override public CodeBuilder fmul() { - writeBytecode(FMUL); + bytecodesBufWriter.writeU1(FMUL); return this; } @Override public CodeBuilder fneg() { - writeBytecode(FNEG); + bytecodesBufWriter.writeU1(FNEG); return this; } @Override public CodeBuilder frem() { - writeBytecode(FREM); + bytecodesBufWriter.writeU1(FREM); + return this; + } + + @Override + public CodeBuilder freturn() { + bytecodesBufWriter.writeU1(FRETURN); return this; } @Override public CodeBuilder fstore(int slot) { - writeLocalVar(BytecodeHelpers.fstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(FSTORE_0 + slot); + } else { + writeLocalVar(FSTORE, slot); + } return this; } @Override public CodeBuilder fsub() { - writeBytecode(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() { - writeBytecode(I2B); + bytecodesBufWriter.writeU1(I2B); return this; } @Override public CodeBuilder i2c() { - writeBytecode(I2C); + bytecodesBufWriter.writeU1(I2C); return this; } @Override public CodeBuilder i2d() { - writeBytecode(I2D); + bytecodesBufWriter.writeU1(I2D); return this; } @Override public CodeBuilder i2f() { - writeBytecode(I2F); + bytecodesBufWriter.writeU1(I2F); return this; } @Override public CodeBuilder i2l() { - writeBytecode(I2L); + bytecodesBufWriter.writeU1(I2L); return this; } @Override public CodeBuilder i2s() { - writeBytecode(I2S); + bytecodesBufWriter.writeU1(I2S); return this; } @Override public CodeBuilder iadd() { - writeBytecode(IADD); + bytecodesBufWriter.writeU1(IADD); return this; } @Override public CodeBuilder iand() { - writeBytecode(IAND); + bytecodesBufWriter.writeU1(IAND); return this; } @Override public CodeBuilder iconst_0() { - writeBytecode(ICONST_0); + bytecodesBufWriter.writeU1(ICONST_0); return this; } @Override public CodeBuilder iconst_1() { - writeBytecode(ICONST_1); + bytecodesBufWriter.writeU1(ICONST_1); return this; } @Override public CodeBuilder iconst_2() { - writeBytecode(ICONST_2); + bytecodesBufWriter.writeU1(ICONST_2); return this; } @Override public CodeBuilder iconst_3() { - writeBytecode(ICONST_3); + bytecodesBufWriter.writeU1(ICONST_3); return this; } @Override public CodeBuilder iconst_4() { - writeBytecode(ICONST_4); + bytecodesBufWriter.writeU1(ICONST_4); return this; } @Override public CodeBuilder iconst_5() { - writeBytecode(ICONST_5); + bytecodesBufWriter.writeU1(ICONST_5); return this; } @Override public CodeBuilder iconst_m1() { - writeBytecode(ICONST_M1); + bytecodesBufWriter.writeU1(ICONST_M1); return this; } @Override public CodeBuilder idiv() { - writeBytecode(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; } @@ -1222,25 +1503,29 @@ public final class DirectCodeBuilder @Override public CodeBuilder iload(int slot) { - writeLocalVar(BytecodeHelpers.iload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ILOAD_0 + slot); + } else { + writeLocalVar(ILOAD, slot); + } return this; } @Override public CodeBuilder imul() { - writeBytecode(IMUL); + bytecodesBufWriter.writeU1(IMUL); return this; } @Override public CodeBuilder ineg() { - writeBytecode(INEG); + bytecodesBufWriter.writeU1(INEG); return this; } @Override public CodeBuilder instanceOf(ClassEntry target) { - writeTypeCheck(INSTANCEOF, target); + bytecodesBufWriter.writeIndex(INSTANCEOF, target); return this; } @@ -1252,85 +1537,95 @@ public final class DirectCodeBuilder @Override public CodeBuilder invokeinterface(InterfaceMethodRefEntry ref) { - writeInvokeInterface(INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1); + writeInvokeInterface(Opcode.INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1); return this; } @Override public CodeBuilder invokespecial(InterfaceMethodRefEntry ref) { - writeInvokeNormal(INVOKESPECIAL, ref); + bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref); return this; } @Override public CodeBuilder invokespecial(MethodRefEntry ref) { - writeInvokeNormal(INVOKESPECIAL, ref); + bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref); return this; } @Override public CodeBuilder invokestatic(InterfaceMethodRefEntry ref) { - writeInvokeNormal(INVOKESTATIC, ref); + bytecodesBufWriter.writeIndex(INVOKESTATIC, ref); return this; } @Override public CodeBuilder invokestatic(MethodRefEntry ref) { - writeInvokeNormal(INVOKESTATIC, ref); + bytecodesBufWriter.writeIndex(INVOKESTATIC, ref); return this; } @Override public CodeBuilder invokevirtual(MethodRefEntry ref) { - writeInvokeNormal(INVOKEVIRTUAL, ref); + bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, ref); return this; } @Override public CodeBuilder ior() { - writeBytecode(IOR); + bytecodesBufWriter.writeU1(IOR); return this; } @Override public CodeBuilder irem() { - writeBytecode(IREM); + bytecodesBufWriter.writeU1(IREM); + return this; + } + + @Override + public CodeBuilder ireturn() { + bytecodesBufWriter.writeU1(IRETURN); return this; } @Override public CodeBuilder ishl() { - writeBytecode(ISHL); + bytecodesBufWriter.writeU1(ISHL); return this; } @Override public CodeBuilder ishr() { - writeBytecode(ISHR); + bytecodesBufWriter.writeU1(ISHR); return this; } @Override public CodeBuilder istore(int slot) { - writeLocalVar(BytecodeHelpers.istore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ISTORE_0 + slot); + } else { + writeLocalVar(ISTORE, slot); + } return this; } @Override public CodeBuilder isub() { - writeBytecode(ISUB); + bytecodesBufWriter.writeU1(ISUB); return this; } @Override public CodeBuilder iushr() { - writeBytecode(IUSHR); + bytecodesBufWriter.writeU1(IUSHR); return this; } @Override public CodeBuilder ixor() { - writeBytecode(IXOR); + bytecodesBufWriter.writeU1(IXOR); return this; } @@ -1342,49 +1637,49 @@ public final class DirectCodeBuilder @Override public CodeBuilder l2d() { - writeBytecode(L2D); + bytecodesBufWriter.writeU1(L2D); return this; } @Override public CodeBuilder l2f() { - writeBytecode(L2F); + bytecodesBufWriter.writeU1(L2F); return this; } @Override public CodeBuilder l2i() { - writeBytecode(L2I); + bytecodesBufWriter.writeU1(L2I); return this; } @Override public CodeBuilder ladd() { - writeBytecode(LADD); + bytecodesBufWriter.writeU1(LADD); return this; } @Override public CodeBuilder land() { - writeBytecode(LAND); + bytecodesBufWriter.writeU1(LAND); return this; } @Override public CodeBuilder lcmp() { - writeBytecode(LCMP); + bytecodesBufWriter.writeU1(LCMP); return this; } @Override public CodeBuilder lconst_0() { - writeBytecode(LCONST_0); + bytecodesBufWriter.writeU1(LCONST_0); return this; } @Override public CodeBuilder lconst_1() { - writeBytecode(LCONST_1); + bytecodesBufWriter.writeU1(LCONST_1); return this; } @@ -1396,85 +1691,99 @@ public final class DirectCodeBuilder @Override public CodeBuilder ldiv() { - writeBytecode(LDIV); + bytecodesBufWriter.writeU1(LDIV); return this; } @Override public CodeBuilder lload(int slot) { - writeLocalVar(BytecodeHelpers.lload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(LLOAD_0 + slot); + } else { + writeLocalVar(LLOAD, slot); + } return this; } @Override public CodeBuilder lmul() { - writeBytecode(LMUL); + bytecodesBufWriter.writeU1(LMUL); return this; } @Override public CodeBuilder lneg() { - writeBytecode(LNEG); + bytecodesBufWriter.writeU1(LNEG); return this; } @Override public CodeBuilder lor() { - writeBytecode(LOR); + bytecodesBufWriter.writeU1(LOR); return this; } @Override public CodeBuilder lrem() { - writeBytecode(LREM); + bytecodesBufWriter.writeU1(LREM); + return this; + } + + @Override + public CodeBuilder lreturn() { + bytecodesBufWriter.writeU1(LRETURN); return this; } @Override public CodeBuilder lshl() { - writeBytecode(LSHL); + bytecodesBufWriter.writeU1(LSHL); return this; } @Override public CodeBuilder lshr() { - writeBytecode(LSHR); + bytecodesBufWriter.writeU1(LSHR); return this; } @Override public CodeBuilder lstore(int slot) { - writeLocalVar(BytecodeHelpers.lstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(LSTORE_0 + slot); + } else { + writeLocalVar(LSTORE, slot); + } return this; } @Override public CodeBuilder lsub() { - writeBytecode(LSUB); + bytecodesBufWriter.writeU1(LSUB); return this; } @Override public CodeBuilder lushr() { - writeBytecode(LUSHR); + bytecodesBufWriter.writeU1(LUSHR); return this; } @Override public CodeBuilder lxor() { - writeBytecode(LXOR); + bytecodesBufWriter.writeU1(LXOR); return this; } @Override public CodeBuilder monitorenter() { - writeBytecode(MONITORENTER); + bytecodesBufWriter.writeU1(MONITORENTER); return this; } @Override public CodeBuilder monitorexit() { - writeBytecode(MONITOREXIT); + bytecodesBufWriter.writeU1(MONITOREXIT); return this; } @@ -1501,26 +1810,26 @@ public final class DirectCodeBuilder @Override public CodeBuilder pop() { - writeBytecode(POP); + bytecodesBufWriter.writeU1(POP); return this; } @Override public CodeBuilder pop2() { - writeBytecode(POP2); + bytecodesBufWriter.writeU1(POP2); return this; } @Override public CodeBuilder sipush(int s) { BytecodeHelpers.validateSipush(s); - writeArgumentConstant(SIPUSH, s); + bytecodesBufWriter.writeU1U2(SIPUSH, s); return this; } @Override public CodeBuilder swap() { - writeBytecode(SWAP); + bytecodesBufWriter.writeU1(SWAP); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java index ec4e148baf3..a841cbf47f4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java @@ -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); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java b/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java index d5ebbbebe7a..be350cb5814 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java @@ -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; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java index 2aaf5f033c0..aac0fafaef1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java @@ -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; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 2193eb76108..0b99766b385 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -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 entryByIndex(int index, Class 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 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> void writeAttribute(BufWriterImpl writer, Attribute attr) { + public static > void writeAttribute(BufWriterImpl writer, Attribute attr) { if (attr instanceof CustomAttribute ca) { var mapper = (AttributeMapper) ca.attributeMapper(); mapper.writeAttribute(writer, (T) ca); @@ -252,11 +252,10 @@ public class Util { } @ForceInline - static void writeList(BufWriterImpl buf, List 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 } } diff --git a/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java index 7d4442c255e..6f59c476a6b 100644 --- a/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java @@ -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; } diff --git a/test/jdk/jdk/classfile/UtilTest.java b/test/jdk/jdk/classfile/UtilTest.java index be66d930580..6ba7e146b03 100644 --- a/test/jdk/jdk/classfile/UtilTest.java +++ b/test/jdk/jdk/classfile/UtilTest.java @@ -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; diff --git a/test/micro/org/openjdk/bench/jdk/classfile/Write.java b/test/micro/org/openjdk/bench/jdk/classfile/Write.java index b8bc9605559..5a966afaebf 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/Write.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/Write.java @@ -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);