1 /*
  2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 package jdk.experimental.bytecode;
 25 
 26 import java.util.function.Consumer;
 27 
 28 /**
 29  * Base class builder. The base of higher level class builders.
 30  *
 31  * @param <S> the type of symbol representation
 32  * @param <T> the type of type descriptors representation
 33  * @param <C> the type of this builder
 34  */
 35 public class ClassBuilder<S, T, C extends ClassBuilder<S, T, C>>
 36         extends DeclBuilder<S, T, byte[], C> {
 37 
 38     /**
 39      * The helper to use to manipulate type descriptors.
 40      */
 41     protected TypeHelper<S, T> typeHelper;
 42 
 43     /**
 44      * The symbol for the class being built.
 45      */
 46     protected S thisClass;
 47 
 48     /**
 49      * The super-interfaces of the class being built..
 50      */
 51     protected GrowableByteBuffer interfaces = new GrowableByteBuffer();
 52 
 53     /**
 54      * The fields of the class being built.
 55      */
 56     protected GrowableByteBuffer fields = new GrowableByteBuffer();
 57 
 58     /**
 59      * The methods of the class being built.
 60      */
 61     protected GrowableByteBuffer methods = new GrowableByteBuffer();
 62 
 63     int majorVersion;
 64     int minorVersion;
 65     int flags;
 66     int superclass;
 67     int nmethods, nfields, ninterfaces;
 68 
 69     /**
 70      * Create a class builder.
 71      *
 72      * @param poolHelper the helper to build the constant pool
 73      * @param typeHelper the helper to use to manipulate type descriptors
 74      */
 75     public ClassBuilder(BytePoolHelper<S, T> poolHelper, TypeHelper<S, T> typeHelper) {
 76         super(poolHelper, typeHelper);
 77         this.typeHelper = typeHelper;
 78     }
 79 
 80     /**
 81      * Set the minor class file version.
 82      *
 83      * @param minorVersion the minor version number
 84      * @return this builder, for chained calls
 85      */
 86     public C withMinorVersion(int minorVersion) {
 87         this.minorVersion = minorVersion;
 88         return thisBuilder();
 89     }
 90 
 91     /**
 92      * Set the major class file version.
 93      *
 94      * @param majorVersion the major version number
 95      * @return this builder, for chained calls
 96      */
 97     public C withMajorVersion(int majorVersion) {
 98         this.majorVersion = majorVersion;
 99         return thisBuilder();
100     }
101 
102     /**
103      * Set the class symbol
104      *
105      * @param thisClass the class symbol
106      * @return this builder, for chained calls
107      */
108     public C withThisClass(S thisClass) {
109         this.thisClass = thisClass;
110         return thisBuilder();
111     }
112 
113     /**
114      * Set the class access flags
115      *
116      * @param flags an array of {@code Flag}
117      * @return this builder, for chained calls
118      */
119     @Override
120     public C withFlags(Flag... flags) {
121         for (Flag f : flags) {
122             this.flags |= f.flag;
123         }
124         return thisBuilder();
125     }
126 
127     /**
128      * Set the superclass
129      *
130      * @param sup the superclass symbol
131      * @return this builder, for chained calls
132      */
133     public C withSuperclass(S sup) {
134         this.superclass = poolHelper.putClass(sup);
135         return thisBuilder();
136     }
137 
138     /**
139      * Add a super interface.
140      *
141      * @param sup an interface symbol
142      * @return this builder, for chained calls
143      */
144     public C withSuperinterface(S sup) {
145         this.interfaces.writeChar(poolHelper.putClass(sup));
146         ninterfaces++;
147         return thisBuilder();
148     }
149 
150     /**
151      * Add a field.
152      *
153      * @param name the name of the field
154      * @param type the type descriptor of the field
155      * @return this builder, for chained calls
156      */
157     public final C withField(CharSequence name, T type) {
158         return withField(name, type, FB -> {
159         });
160     }
161 
162     /**
163      * Add a field.
164      *
165      * @param name the name of the field
166      * @param type the type descriptor of the field
167      * @param fieldConfig access to the {@code FieldBuilder} to allow clients to
168      * adjust flags, annotations and bytecode attributes on the field declaration
169      * @return this builder, for chained calls
170      */
171     public C withField(CharSequence name, T type, Consumer<? super FieldBuilder<S, T, byte[]>> fieldConfig) {
172         FieldBuilder<S, T, byte[]> F = new FieldBuilder<>(name, type, poolHelper, typeHelper);
173         fieldConfig.accept(F);
174         F.build(fields);
175         nfields++;
176         return thisBuilder();
177     }
178 
179     /**
180      * Add a method
181      *
182      * @param name the name of the method
183      * @param type the type descriptor of the method
184      * @return this builder, for chained calls
185      */
186     public final C withMethod(CharSequence name, T type) {
187         return withMethod(name, type, MB -> {
188         });
189     }
190 
191     /**
192      * Add a method
193      *
194      * @param name the name of the method
195      * @param type the type descriptor of the method
196      * @param methodConfig access to the {@code MethodBuilder} to allow clients to
197      * adjust flags, annotations and bytecode attributes on the method declaration
198      * @return this builder, for chained calls
199      */
200     public C withMethod(CharSequence name, T type, Consumer<? super MethodBuilder<S, T, byte[]>> methodConfig) {
201         MethodBuilder<S, T, byte[]> M = new MethodBuilder<>(thisClass, name, type, poolHelper, typeHelper);
202         methodConfig.accept(M);
203         M.build(methods);
204         nmethods++;
205         return thisBuilder();
206     }
207 
208     /**
209      * Build the constant pool into a byte array.
210      *
211      * @return a representation of this constant pool as a byte array
212      */
213     @SuppressWarnings("unchecked")
214     public byte[] build() {
215         ((BytePoolHelper<S, T>)poolHelper).addAttributes(this);
216         addAnnotations();
217         int thisClassIdx = poolHelper.putClass(thisClass);
218         byte[] poolBytes = poolHelper.entries();
219         GrowableByteBuffer buf = new GrowableByteBuffer();
220         buf.writeInt(0xCAFEBABE);
221         buf.writeChar(minorVersion);
222         buf.writeChar(majorVersion);
223         buf.writeChar(poolHelper.size() + 1);
224         buf.writeBytes(poolBytes);
225         buf.writeChar(flags);
226         buf.writeChar(thisClassIdx);
227         buf.writeChar(superclass);
228         buf.writeChar(ninterfaces);
229         if (ninterfaces > 0) {
230             buf.writeBytes(interfaces);
231         }
232         buf.writeChar(nfields);
233         buf.writeBytes(fields);
234         buf.writeChar(nmethods);
235         buf.writeBytes(methods);
236         buf.writeChar(nattrs);
237         buf.writeBytes(attributes);
238         return buf.bytes();
239     }
240 }