1 /* 2 * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package jdk.internal.classfile.impl; 28 29 import java.lang.classfile.*; 30 import java.lang.classfile.constantpool.ClassEntry; 31 import java.lang.classfile.constantpool.Utf8Entry; 32 import java.lang.constant.ConstantDescs; 33 import java.util.Arrays; 34 import java.util.Collections; 35 import java.util.List; 36 import java.util.function.Consumer; 37 38 import static java.lang.classfile.ClassFile.PREVIEW_MINOR_VERSION; 39 import static java.lang.classfile.ClassFile.latestMajorVersion; 40 import static java.util.Objects.requireNonNull; 41 42 public final class DirectClassBuilder 43 extends AbstractDirectBuilder<ClassModel> 44 implements ClassBuilder { 45 46 /** The value of default class access flags */ 47 static final int DEFAULT_CLASS_FLAGS = ClassFile.ACC_PUBLIC | ClassFile.ACC_SUPER; 48 static final Util.Writable[] EMPTY_WRITABLE_ARRAY = {}; 49 static final WritableField[] EMPTY_WRITABLE_FIELD_ARRAY = {}; 50 static final ClassEntry[] EMPTY_CLASS_ENTRY_ARRAY = {}; 51 final ClassEntry thisClassEntry; 52 private WritableField[] fields = EMPTY_WRITABLE_FIELD_ARRAY; 53 private Util.Writable[] methods = EMPTY_WRITABLE_ARRAY; 54 private int fieldsCount = 0; 55 private int methodsCount = 0; 56 private ClassEntry superclassEntry; 57 private List<ClassEntry> interfaceEntries; 58 private int majorVersion; 59 private int minorVersion; 60 private int flags; 61 private int sizeHint; 62 63 public DirectClassBuilder(SplitConstantPool constantPool, 64 ClassFileImpl context, 65 ClassEntry thisClass) { 66 super(constantPool, context); 67 this.thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClass); 68 this.flags = DEFAULT_CLASS_FLAGS; 69 this.superclassEntry = null; 70 this.interfaceEntries = Collections.emptyList(); 71 this.majorVersion = ClassFile.latestMajorVersion(); 72 this.minorVersion = ClassFile.latestMinorVersion(); 73 } 74 75 @Override 76 public ClassBuilder with(ClassElement element) { 77 if (element instanceof AbstractElement ae) { 78 ae.writeTo(this); 79 } else { 80 writeAttribute((CustomAttribute<?>) requireNonNull(element)); 81 } 82 return this; 83 } 84 85 @Override 86 public ClassBuilder withFlags(int flags) { 87 setFlags(Util.checkFlags(flags)); 88 return this; 89 } 90 91 @Override 92 public ClassBuilder withField(Utf8Entry name, 93 Utf8Entry descriptor, 94 int flags) { 95 return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, flags, null)); 96 } 97 98 @Override 99 public ClassBuilder withField(Utf8Entry name, 100 Utf8Entry descriptor, 101 Consumer<? super FieldBuilder> handler) { 102 return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, 0, null) 103 .run(handler)); 104 } 105 106 @Override 107 public ClassBuilder transformField(FieldModel field, FieldTransform transform) { 108 DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, context, field.fieldName(), 109 field.fieldType(), 0, field); 110 builder.transform(field, transform); 111 return withField(builder); 112 } 113 114 @Override 115 public ClassBuilder withMethod(Utf8Entry name, 116 Utf8Entry descriptor, 117 int flags, 118 Consumer<? super MethodBuilder> handler) { 119 return withMethod(new DirectMethodBuilder(constantPool, context, name, descriptor, flags, null) 120 .run(handler)); 121 } 122 123 @Override 124 public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) { 125 DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, context, method.methodName(), 126 method.methodType(), 127 method.flags().flagsMask(), 128 method); 129 builder.transform(method, transform); 130 return withMethod(builder); 131 } 132 133 // internal / for use by elements 134 135 ClassBuilder withField(WritableField field) { 136 if (fieldsCount >= fields.length) { 137 int newCapacity = fieldsCount + 8; 138 this.fields = Arrays.copyOf(fields, newCapacity); 139 } 140 fields[fieldsCount++] = field; 141 return this; 142 } 143 144 ClassBuilder withMethod(Util.Writable method) { 145 if (methodsCount >= methods.length) { 146 int newCapacity = methodsCount + 8; 147 this.methods = Arrays.copyOf(methods, newCapacity); 148 } 149 methods[methodsCount++] = method; 150 return this; 151 } 152 153 void setSuperclass(ClassEntry superclassEntry) { 154 this.superclassEntry = superclassEntry; 155 } 156 157 void setInterfaces(List<ClassEntry> interfaces) { 158 this.interfaceEntries = interfaces; 159 } 160 161 void setVersion(int major, int minor) { 162 this.majorVersion = major; 163 this.minorVersion = minor; 164 } 165 166 void setFlags(int flags) { 167 this.flags = flags; 168 } 169 170 public void setSizeHint(int sizeHint) { 171 this.sizeHint = sizeHint; 172 } 173 174 public byte[] build() { 175 176 // The logic of this is very carefully ordered. We want to avoid 177 // repeated buffer copyings, so we accumulate lists of writers which 178 // all get written later into the same buffer. But, writing can often 179 // trigger CP / BSM insertions, so we cannot run the CP writer or 180 // BSM writers until everything else is written. 181 182 // Do this early because it might trigger CP activity 183 var constantPool = this.constantPool; 184 ClassEntry superclass = superclassEntry; 185 if (superclass != null) 186 superclass = AbstractPoolEntry.maybeClone(constantPool, superclass); 187 else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisClassEntry.asInternalName())) 188 superclass = constantPool.classEntry(ConstantDescs.CD_Object); 189 int interfaceEntriesSize = interfaceEntries.size(); 190 ClassEntry[] ies = interfaceEntriesSize == 0 ? EMPTY_CLASS_ENTRY_ARRAY : buildInterfaceEnties(interfaceEntriesSize); 191 192 // We maintain two writers, and then we join them at the end 193 int size = sizeHint == 0 ? 256 : sizeHint; 194 BufWriterImpl head = new BufWriterImpl(constantPool, context, size); 195 BufWriterImpl tail = new BufWriterImpl(constantPool, context, size, thisClassEntry, majorVersion); 196 197 // The tail consists of fields and methods, and attributes 198 // This should trigger all the CP/BSM mutation 199 Util.writeList(tail, fields, fieldsCount); 200 WritableField.UnsetField[] strictInstanceFields; 201 if (minorVersion == PREVIEW_MINOR_VERSION && majorVersion >= Util.VALUE_OBJECTS_MAJOR) { 202 strictInstanceFields = WritableField.filterStrictInstanceFields(constantPool, fields, fieldsCount); 203 } else { 204 strictInstanceFields = WritableField.UnsetField.EMPTY_ARRAY; 205 } 206 tail.setStrictInstanceFields(strictInstanceFields); 207 Util.writeList(tail, methods, methodsCount); 208 int attributesOffset = tail.size(); 209 attributes.writeTo(tail); 210 211 // Now we have to append the BSM, if there is one 212 if (constantPool.writeBootstrapMethods(tail)) { 213 // Update attributes count 214 tail.patchU2(attributesOffset, attributes.size() + 1); 215 } 216 217 // Now we can make the head 218 head.writeInt(ClassFile.MAGIC_NUMBER); 219 head.writeU2U2(minorVersion, majorVersion); 220 constantPool.writeTo(head); 221 head.writeU2U2U2(flags, head.cpIndex(thisClassEntry), head.cpIndexOrZero(superclass)); 222 head.writeU2(interfaceEntriesSize); 223 for (int i = 0; i < interfaceEntriesSize; i++) { 224 head.writeIndex(ies[i]); 225 } 226 227 // Join head and tail into an exact-size buffer 228 return BufWriterImpl.join(head, tail); 229 } 230 231 private ClassEntry[] buildInterfaceEnties(int interfaceEntriesSize) { 232 var ies = new ClassEntry[interfaceEntriesSize]; 233 for (int i = 0; i < interfaceEntriesSize; i++) 234 ies[i] = AbstractPoolEntry.maybeClone(constantPool, interfaceEntries.get(i)); 235 return ies; 236 } 237 }