1 /* 2 * Copyright (c) 2022, 2024, 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 ClassEntry[] EMPTY_CLASS_ENTRY_ARRAY = {}; 48 final ClassEntry thisClassEntry; 49 private Util.Writable[] fields = EMPTY_WRITABLE_ARRAY; 50 private Util.Writable[] methods = EMPTY_WRITABLE_ARRAY; 51 private int fieldsCount = 0; 52 private int methodsCount = 0; 53 private ClassEntry superclassEntry; 54 private List<ClassEntry> interfaceEntries; 55 private int majorVersion; 56 private int minorVersion; 57 private int flags; 58 private int sizeHint; 59 60 public DirectClassBuilder(SplitConstantPool constantPool, 61 ClassFileImpl context, 62 ClassEntry thisClass) { 63 super(constantPool, context); 64 this.thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClass); 65 this.flags = DEFAULT_CLASS_FLAGS; 66 this.superclassEntry = null; 67 this.interfaceEntries = Collections.emptyList(); 68 this.majorVersion = ClassFile.latestMajorVersion(); 69 this.minorVersion = ClassFile.latestMinorVersion(); 70 } 71 72 @Override 73 public ClassBuilder with(ClassElement element) { 74 if (element instanceof AbstractElement ae) { 75 ae.writeTo(this); 76 } else { 77 writeAttribute((CustomAttribute<?>) requireNonNull(element)); 78 } 79 return this; 80 } 81 82 @Override 83 public ClassBuilder withFlags(int flags) { 84 setFlags(flags); 85 return this; 86 } 87 88 @Override 89 public ClassBuilder withField(Utf8Entry name, 90 Utf8Entry descriptor, 91 int flags) { 92 return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, flags, null)); 93 } 94 95 @Override 96 public ClassBuilder withField(Utf8Entry name, 97 Utf8Entry descriptor, 98 Consumer<? super FieldBuilder> handler) { 99 return withField(new DirectFieldBuilder(constantPool, context, name, descriptor, 0, null) 100 .run(handler)); 101 } 102 103 @Override 104 public ClassBuilder transformField(FieldModel field, FieldTransform transform) { 105 DirectFieldBuilder builder = new DirectFieldBuilder(constantPool, context, field.fieldName(), 106 field.fieldType(), 0, field); 107 builder.transform(field, transform); 108 return withField(builder); 109 } 110 111 @Override 112 public ClassBuilder withMethod(Utf8Entry name, 113 Utf8Entry descriptor, 114 int flags, 115 Consumer<? super MethodBuilder> handler) { 116 return withMethod(new DirectMethodBuilder(constantPool, context, name, descriptor, flags, null) 117 .run(handler)); 118 } 119 120 @Override 121 public ClassBuilder transformMethod(MethodModel method, MethodTransform transform) { 122 DirectMethodBuilder builder = new DirectMethodBuilder(constantPool, context, method.methodName(), 123 method.methodType(), 124 method.flags().flagsMask(), 125 method); 126 builder.transform(method, transform); 127 return withMethod(builder); 128 } 129 130 // internal / for use by elements 131 132 ClassBuilder withField(Util.Writable field) { 133 if (fieldsCount >= fields.length) { 134 int newCapacity = fieldsCount + 8; 135 this.fields = Arrays.copyOf(fields, newCapacity); 136 } 137 fields[fieldsCount++] = field; 138 return this; 139 } 140 141 ClassBuilder withMethod(Util.Writable method) { 142 if (methodsCount >= methods.length) { 143 int newCapacity = methodsCount + 8; 144 this.methods = Arrays.copyOf(methods, newCapacity); 145 } 146 methods[methodsCount++] = method; 147 return this; 148 } 149 150 void setSuperclass(ClassEntry superclassEntry) { 151 this.superclassEntry = superclassEntry; 152 } 153 154 void setInterfaces(List<ClassEntry> interfaces) { 155 this.interfaceEntries = interfaces; 156 } 157 158 void setVersion(int major, int minor) { 159 this.majorVersion = major; 160 this.minorVersion = minor; 161 } 162 163 void setFlags(int flags) { 164 this.flags = flags; 165 } 166 167 public void setSizeHint(int sizeHint) { 168 this.sizeHint = sizeHint; 169 } 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 Util.writeList(tail, methods, methodsCount); 199 int attributesOffset = tail.size(); 200 attributes.writeTo(tail); 201 202 // Now we have to append the BSM, if there is one 203 if (constantPool.writeBootstrapMethods(tail)) { 204 // Update attributes count 205 tail.patchU2(attributesOffset, attributes.size() + 1); 206 } 207 208 // Now we can make the head 209 head.writeInt(ClassFile.MAGIC_NUMBER); 210 head.writeU2U2(minorVersion, majorVersion); 211 constantPool.writeTo(head); 212 head.writeU2U2U2(flags, head.cpIndex(thisClassEntry), head.cpIndexOrZero(superclass)); 213 head.writeU2(interfaceEntriesSize); 214 for (int i = 0; i < interfaceEntriesSize; i++) { 215 head.writeIndex(ies[i]); 216 } 217 218 // Join head and tail into an exact-size buffer 219 return BufWriterImpl.join(head, tail); 220 } 221 222 private ClassEntry[] buildInterfaceEnties(int interfaceEntriesSize) { 223 var ies = new ClassEntry[interfaceEntriesSize]; 224 for (int i = 0; i < interfaceEntriesSize; i++) 225 ies[i] = AbstractPoolEntry.maybeClone(constantPool, interfaceEntries.get(i)); 226 return ies; 227 } 228 }