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.util.Objects.requireNonNull; 39 40 public final class DirectClassBuilder 41 extends AbstractDirectBuilder<ClassModel> 42 implements ClassBuilder { 43 44 /** The value of default class access flags */ 45 static final int DEFAULT_CLASS_FLAGS = ClassFile.ACC_PUBLIC; 46 static final Util.Writable[] EMPTY_WRITABLE_ARRAY = {}; 47 static final WritableField[] EMPTY_WRITABLE_FIELD_ARRAY = {}; 48 static final ClassEntry[] EMPTY_CLASS_ENTRY_ARRAY = {}; 49 final ClassEntry thisClassEntry; 50 private WritableField[] fields = EMPTY_WRITABLE_FIELD_ARRAY; 51 private Util.Writable[] methods = EMPTY_WRITABLE_ARRAY; 52 private int fieldsCount = 0; 53 private int methodsCount = 0; 54 private ClassEntry superclassEntry; 55 private List<ClassEntry> interfaceEntries; 56 private int majorVersion; 57 private int minorVersion; 58 private int flags; 59 private int sizeHint; 60 61 public DirectClassBuilder(SplitConstantPool constantPool, 62 ClassFileImpl context, 63 ClassEntry thisClass) { 64 super(constantPool, context); 65 this.thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClass); 66 this.flags = DEFAULT_CLASS_FLAGS; 67 this.superclassEntry = null; 68 this.interfaceEntries = Collections.emptyList(); 69 this.majorVersion = ClassFile.latestMajorVersion(); 70 this.minorVersion = ClassFile.latestMinorVersion(); 71 } 72 73 @Override 74 public ClassBuilder with(ClassElement element) { 75 if (element instanceof AbstractElement ae) { 76 ae.writeTo(this); 77 } else { 78 writeAttribute((CustomAttribute<?>) requireNonNull(element)); 79 } 80 return this; 81 } 82 83 @Override 84 public ClassBuilder withFlags(int flags) { 85 setFlags(flags); 86 return this; 87 } 88 89 @Override 90 public ClassBuilder withField(Utf8Entry name, 91 Utf8Entry descriptor, 92 int flags) { 93 return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, flags, null)); 94 } 95 96 @Override 97 public ClassBuilder withField(Utf8Entry name, 98 Utf8Entry descriptor, 99 Consumer<? super FieldBuilder> handler) { 100 return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, 0, null) 101 .run(handler)); 102 } 103 104 @Override 105 public ClassBuilder transformField(FieldModel field, FieldTransform transform) { 106 DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, context, field.fieldName(), 107 field.fieldType(), 0, field); 108 builder.transform(field, transform); 109 return withField(builder); 110 } 111 112 @Override 113 public ClassBuilder withMethod(Utf8Entry name, 114 Utf8Entry descriptor, 115 int flags, 116 Consumer<? super MethodBuilder> handler) { 117 return withMethod(new DirectMethodBuilder(constantPool, context, name, descriptor, flags, null) 118 .run(handler)); 119 } 120 121 @Override 122 public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) { 123 DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, context, method.methodName(), 124 method.methodType(), 125 method.flags().flagsMask(), 126 method); 127 builder.transform(method, transform); 128 return withMethod(builder); 129 } 130 131 // internal / for use by elements 132 133 ClassBuilder withField(WritableField field) { 134 if (fieldsCount >= fields.length) { 135 int newCapacity = fieldsCount + 8; 136 this.fields = Arrays.copyOf(fields, newCapacity); 137 } 138 fields[fieldsCount++] = field; 139 return this; 140 } 141 142 ClassBuilder withMethod(Util.Writable method) { 143 if (methodsCount >= methods.length) { 144 int newCapacity = methodsCount + 8; 145 this.methods = Arrays.copyOf(methods, newCapacity); 146 } 147 methods[methodsCount++] = method; 148 return this; 149 } 150 151 void setSuperclass(ClassEntry superclassEntry) { 152 this.superclassEntry = superclassEntry; 153 } 154 155 void setInterfaces(List<ClassEntry> interfaces) { 156 this.interfaceEntries = interfaces; 157 } 158 159 void setVersion(int major, int minor) { 160 this.majorVersion = major; 161 this.minorVersion = minor; 162 } 163 164 void setFlags(int flags) { 165 this.flags = flags; 166 } 167 168 public void setSizeHint(int sizeHint) { 169 this.sizeHint = sizeHint; 170 } 171 172 public byte[] build() { 173 174 // The logic of this is very carefully ordered. We want to avoid 175 // repeated buffer copyings, so we accumulate lists of writers which 176 // all get written later into the same buffer. But, writing can often 177 // trigger CP / BSM insertions, so we cannot run the CP writer or 178 // BSM writers until everything else is written. 179 180 // Do this early because it might trigger CP activity 181 var constantPool = this.constantPool; 182 ClassEntry superclass = superclassEntry; 183 if (superclass != null) 184 superclass = AbstractPoolEntry.maybeClone(constantPool, superclass); 185 else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisClassEntry.asInternalName())) 186 superclass = constantPool.classEntry(ConstantDescs.CD_Object); 187 int interfaceEntriesSize = interfaceEntries.size(); 188 ClassEntry[] ies = interfaceEntriesSize == 0 ? EMPTY_CLASS_ENTRY_ARRAY : buildInterfaceEnties(interfaceEntriesSize); 189 190 // We maintain two writers, and then we join them at the end 191 int size = sizeHint == 0 ? 256 : sizeHint; 192 BufWriterImpl head = new BufWriterImpl(constantPool, context, size); 193 BufWriterImpl tail = new BufWriterImpl(constantPool, context, size, thisClassEntry, majorVersion); 194 195 // The tail consists of fields and methods, and attributes 196 // This should trigger all the CP/BSM mutation 197 Util.writeList(tail, fields, fieldsCount); 198 var strictInstanceFields = WritableField.filterStrictInstanceFields(constantPool, fields, fieldsCount); 199 tail.setStrictInstanceFields(strictInstanceFields); 200 Util.writeList(tail, methods, methodsCount); 201 int attributesOffset = tail.size(); 202 attributes.writeTo(tail); 203 204 // Now we have to append the BSM, if there is one 205 if (constantPool.writeBootstrapMethods(tail)) { 206 // Update attributes count 207 tail.patchU2(attributesOffset, attributes.size() + 1); 208 } 209 210 // Now we can make the head 211 head.writeInt(ClassFile.MAGIC_NUMBER); 212 head.writeU2U2(minorVersion, majorVersion); 213 constantPool.writeTo(head); 214 head.writeU2U2U2(flags, head.cpIndex(thisClassEntry), head.cpIndexOrZero(superclass)); 215 head.writeU2(interfaceEntriesSize); 216 for (int i = 0; i < interfaceEntriesSize; i++) { 217 head.writeIndex(ies[i]); 218 } 219 220 // Join head and tail into an exact-size buffer 221 return BufWriterImpl.join(head, tail); 222 } 223 224 private ClassEntry[] buildInterfaceEnties(int interfaceEntriesSize) { 225 var ies = new ClassEntry[interfaceEntriesSize]; 226 for (int i = 0; i < interfaceEntriesSize; i++) 227 ies[i] = AbstractPoolEntry.maybeClone(constantPool, interfaceEntries.get(i)); 228 return ies; 229 } 230 }