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 import java.util.function.ToIntBiFunction;
 28 
 29 public class AnnotationsBuilder<S, T, E> extends AbstractBuilder<S, T, E, AnnotationsBuilder<S, T, E>> {
 30 
 31     GrowableByteBuffer annoAttribute;
 32     int nannos;
 33 
 34     AnnotationsBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
 35         super(poolHelper, typeHelper);
 36         this.annoAttribute = new GrowableByteBuffer();
 37         annoAttribute.writeChar(0);
 38     }
 39 
 40     public enum Kind {
 41         RUNTIME_VISIBLE,
 42         RUNTIME_INVISIBLE;
 43     }
 44 
 45     enum Tag {
 46         B('B'),
 47         C('C'),
 48         D('D'),
 49         F('F'),
 50         I('I'),
 51         J('J'),
 52         S('S'),
 53         Z('Z'),
 54         STRING('s'),
 55         ENUM('e'),
 56         CLASS('c'),
 57         ANNO('@'),
 58         ARRAY('[');
 59 
 60         char tagChar;
 61 
 62         Tag(char tagChar) {
 63             this.tagChar = tagChar;
 64         }
 65     }
 66 
 67     AnnotationsBuilder<S, T, E> withAnnotation(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
 68         annoAttribute.writeChar(poolHelper.putType(annoType));
 69         int offset = annoAttribute.offset;
 70         annoAttribute.writeChar(0);
 71         if (annotationBuilder != null) {
 72             AnnotationElementBuilder _builder = new AnnotationElementBuilder();
 73             int nelems = _builder.withElements(annotationBuilder);
 74             patchCharAt(offset, nelems);
 75         }
 76         nannos++;
 77         return this;
 78     }
 79 
 80     byte[] build() {
 81         patchCharAt(0, nannos);
 82         return annoAttribute.bytes();
 83     }
 84 
 85     private void patchCharAt(int offset, int newChar) {
 86         int prevOffset = annoAttribute.offset;
 87         try {
 88             annoAttribute.offset = offset;
 89             annoAttribute.writeChar(newChar);
 90         } finally {
 91             annoAttribute.offset = prevOffset;
 92         }
 93     }
 94 
 95     @SuppressWarnings({"unchecked", "rawtypes"})
 96     static Consumer NO_BUILDER =
 97             new Consumer() {
 98                 @Override
 99                 public void accept(Object o) {
100                     //do nothing
101                 }
102             };
103 
104     public class AnnotationElementBuilder {
105 
106         int nelems;
107 
108         public AnnotationElementBuilder withString(String name, String s) {
109             annoAttribute.writeChar(poolHelper.putUtf8(name));
110             writeStringValue(s);
111             return this;
112         }
113 
114         private void writeStringValue(String s) {
115             annoAttribute.writeByte(Tag.STRING.tagChar);
116             annoAttribute.writeChar(poolHelper.putUtf8(s));
117             nelems++;
118         }
119 
120         public AnnotationElementBuilder withClass(String name, T s) {
121             annoAttribute.writeChar(poolHelper.putUtf8(name));
122             writeClassValue(s);
123             return this;
124         }
125 
126         private void writeClassValue(T s) {
127             annoAttribute.writeByte(Tag.CLASS.tagChar);
128             annoAttribute.writeChar(poolHelper.putType(s));
129             nelems++;
130         }
131 
132         public AnnotationElementBuilder withEnum(String name, T enumType, int constant) {
133             annoAttribute.writeChar(poolHelper.putUtf8(name));
134             writeEnumValue(enumType, constant);
135             return this;
136         }
137 
138         private void writeEnumValue(T enumType, int constant) {
139             annoAttribute.writeByte(Tag.ENUM.tagChar);
140             annoAttribute.writeChar(poolHelper.putType(enumType));
141             annoAttribute.writeChar(constant);
142             nelems++;
143         }
144 
145         public AnnotationElementBuilder withAnnotation(String name, T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
146             annoAttribute.writeChar(poolHelper.putUtf8(name));
147             writeAnnotationValue(annoType, annotationBuilder);
148             return this;
149         }
150 
151         private void writeAnnotationValue(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
152             annoAttribute.writeByte(Tag.ANNO.tagChar);
153             annoAttribute.writeChar(poolHelper.putType(annoType));
154             int offset = annoAttribute.offset;
155             annoAttribute.writeChar(0);
156             int nelems = withNestedElements(annotationBuilder);
157             patchCharAt(offset, nelems);
158             this.nelems++;
159         }
160 
161         public AnnotationElementBuilder withPrimitive(String name, char c) {
162             annoAttribute.writeChar(poolHelper.putUtf8(name));
163             writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
164             return this;
165         }
166 
167         public AnnotationElementBuilder withPrimitive(String name, short s) {
168             annoAttribute.writeChar(poolHelper.putUtf8(name));
169             writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
170             return this;
171         }
172 
173         public AnnotationElementBuilder withPrimitive(String name, byte b) {
174             annoAttribute.writeChar(poolHelper.putUtf8(name));
175             writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
176             return this;
177         }
178 
179         public AnnotationElementBuilder withPrimitive(String name, int i) {
180             annoAttribute.writeChar(poolHelper.putUtf8(name));
181             writePrimitiveValue(Tag.I, i, PoolHelper::putInt);
182             return this;
183         }
184 
185         public AnnotationElementBuilder withPrimitive(String name, float f) {
186             annoAttribute.writeChar(poolHelper.putUtf8(name));
187             writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
188             return this;
189         }
190 
191         public AnnotationElementBuilder withPrimitive(String name, long l) {
192             annoAttribute.writeChar(poolHelper.putUtf8(name));
193             writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
194             return this;
195         }
196 
197         public AnnotationElementBuilder withPrimitive(String name, double d) {
198             annoAttribute.writeChar(poolHelper.putUtf8(name));
199             writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
200             return this;
201         }
202 
203         public AnnotationElementBuilder withPrimitive(String name, boolean b) {
204             annoAttribute.writeChar(poolHelper.putUtf8(name));
205             writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
206             return this;
207         }
208 
209         private <Z> void writePrimitiveValue(Tag tag, Z value, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc) {
210             annoAttribute.writeByte(tag.tagChar);
211             annoAttribute.writeChar(poolFunc.applyAsInt(poolHelper, value));
212             nelems++;
213         }
214 
215         AnnotationElementBuilder withStrings(String name, String... ss) {
216             annoAttribute.writeChar(poolHelper.putUtf8(name));
217             annoAttribute.writeChar(ss.length);
218             for (String s : ss) {
219                 writeStringValue(s);
220             }
221             return this;
222         }
223 
224         @SuppressWarnings("unchecked")
225         AnnotationElementBuilder withClasses(String name, T... cc) {
226             annoAttribute.writeChar(poolHelper.putUtf8(name));
227             annoAttribute.writeChar(cc.length);
228             for (T c : cc) {
229                 writeClassValue(c);
230             }
231             return this;
232         }
233 
234         AnnotationElementBuilder withEnums(String name, T enumType, int... constants) {
235             annoAttribute.writeChar(poolHelper.putUtf8(name));
236             annoAttribute.writeChar(constants.length);
237             for (int c : constants) {
238                 writeEnumValue(enumType, c);
239             }
240             return this;
241         }
242 
243         @SuppressWarnings("unchecked")
244         public AnnotationElementBuilder withAnnotations(String name, T annoType, Consumer<? super AnnotationElementBuilder>... annotationBuilders) {
245             annoAttribute.writeChar(poolHelper.putUtf8(name));
246             annoAttribute.writeChar(annotationBuilders.length);
247             for (Consumer<? super AnnotationElementBuilder> annotationBuilder : annotationBuilders) {
248                 writeAnnotationValue(annoType, annotationBuilder);
249             }
250             return this;
251         }
252 
253         public AnnotationElementBuilder withPrimitives(String name, char... cc) {
254             annoAttribute.writeChar(poolHelper.putUtf8(name));
255             annoAttribute.writeChar(cc.length);
256             for (char c : cc) {
257                 writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
258             }
259             return this;
260         }
261 
262         public AnnotationElementBuilder withPrimitives(String name, short... ss) {
263             annoAttribute.writeChar(poolHelper.putUtf8(name));
264             annoAttribute.writeChar(ss.length);
265             for (short s : ss) {
266                 writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
267             }
268             return this;
269         }
270 
271         public AnnotationElementBuilder withPrimitives(String name, byte... bb) {
272             annoAttribute.writeChar(poolHelper.putUtf8(name));
273             annoAttribute.writeChar(bb.length);
274             for (byte b : bb) {
275                 writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
276             }
277             return this;
278         }
279 
280         public AnnotationElementBuilder withPrimitives(String name, int... ii) {
281             annoAttribute.writeChar(poolHelper.putUtf8(name));
282             annoAttribute.writeChar(ii.length);
283             for (int i : ii) {
284                 writePrimitiveValue(Tag.I, i,  PoolHelper::putInt);
285             }
286             return this;
287         }
288 
289         public AnnotationElementBuilder withPrimitives(String name, float... ff) {
290             annoAttribute.writeChar(poolHelper.putUtf8(name));
291             annoAttribute.writeChar(ff.length);
292             for (float f : ff) {
293                 writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
294             }
295             return this;
296         }
297 
298         public AnnotationElementBuilder withPrimitives(String name, long... ll) {
299             annoAttribute.writeChar(poolHelper.putUtf8(name));
300             annoAttribute.writeChar(ll.length);
301             for (long l : ll) {
302                 writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
303             }
304             return this;
305         }
306 
307         public AnnotationElementBuilder withPrimitives(String name, double... dd) {
308             annoAttribute.writeChar(poolHelper.putUtf8(name));
309             annoAttribute.writeChar(dd.length);
310             for (double d : dd) {
311                 writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
312             }
313             return this;
314         }
315 
316         public AnnotationElementBuilder withPrimitives(String name, boolean... bb) {
317             annoAttribute.writeChar(poolHelper.putUtf8(name));
318             annoAttribute.writeChar(bb.length);
319             for (boolean b : bb) {
320                 writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
321             }
322             return this;
323         }
324 
325         int withNestedElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
326             return withElements(new AnnotationElementBuilder(), annotationBuilder);
327         }
328 
329         int withElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
330             return withElements(this, annotationBuilder);
331         }
332 
333         private int withElements(AnnotationElementBuilder builder, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
334             annotationBuilder.accept(builder);
335             return builder.nelems;
336         }
337     }
338 }