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.lang.invoke.MethodHandleInfo;
 27 import java.lang.invoke.MethodType;
 28 import java.util.ArrayList;
 29 import java.util.List;
 30 import java.util.Objects;
 31 import java.util.function.Consumer;
 32 import java.util.function.Function;
 33 import java.util.function.ToIntBiFunction;
 34 
 35 /**
 36  * A helper for building and tracking constant pools whose entries are
 37  * represented as byte arrays.
 38  *
 39  * @param <S> the type of the symbol representation
 40  * @param <T> the type of type descriptors representation
 41  */
 42 public class BytePoolHelper<S, T> implements PoolHelper<S, T, byte[]> {
 43 
 44     GrowableByteBuffer pool = new GrowableByteBuffer();
 45     GrowableByteBuffer bsm_attr = new GrowableByteBuffer();
 46     //Map<PoolKey, PoolKey> indicesMap = new HashMap<>();
 47     int currentIndex = 1;
 48     int currentBsmIndex = 0;
 49 
 50     KeyMap<PoolKey> entries = new KeyMap<>();
 51     KeyMap<BsmKey> bootstraps = new KeyMap<>();
 52     PoolKey key = new PoolKey();
 53     BsmKey bsmKey = new BsmKey();
 54 
 55     Function<S, String> symbolToString;
 56     Function<T, String> typeToString;
 57 
 58     public BytePoolHelper(Function<S, String> symbolToString, Function<T, String> typeToString) {
 59         this.symbolToString = symbolToString;
 60         this.typeToString = typeToString;
 61     }
 62 
 63     static class KeyMap<K extends AbstractKey<K>> {
 64 
 65         @SuppressWarnings("unchecked")
 66         K[] table = (K[])new AbstractKey<?>[0x10];
 67         int nelems;
 68 
 69         public void enter(K e) {
 70             if (nelems * 3 >= (table.length - 1) * 2)
 71                 dble();
 72             int hash = getIndex(e);
 73             K old = table[hash];
 74             if (old == null) {
 75                 nelems++;
 76             }
 77             e.next = old;
 78             table[hash] = e;
 79         }
 80 
 81         protected K lookup(K other) {
 82             K e = table[getIndex(other)];
 83             while (e != null && !e.equals(other))
 84                 e = e.next;
 85             return e;
 86         }
 87 
 88         /**
 89          * Look for slot in the table.
 90          * We use open addressing with double hashing.
 91          */
 92         int getIndex(K e) {
 93             int hashMask = table.length - 1;
 94             int h = e.hashCode();
 95             int i = h & hashMask;
 96             // The expression below is always odd, so it is guaranteed
 97             // to be mutually prime with table.length, a power of 2.
 98             int x = hashMask - ((h + (h >> 16)) << 1);
 99             for (; ; ) {
100                 K e2 = table[i];
101                 if (e2 == null)
102                     return i;
103                 else if (e.hash == e2.hash)
104                     return i;
105                 i = (i + x) & hashMask;
106             }
107         }
108 
109         @SuppressWarnings("unchecked")
110         private void dble() {
111             K[] oldtable = table;
112             table = (K[])new AbstractKey<?>[oldtable.length * 2];
113             int n = 0;
114             for (int i = oldtable.length; --i >= 0; ) {
115                 K e = oldtable[i];
116                 if (e != null) {
117                     table[getIndex(e)] = e;
118                     n++;
119                 }
120             }
121             // We don't need to update nelems for shared inherited scopes,
122             // since that gets handled by leave().
123             nelems = n;
124         }
125     }
126 
127     public static abstract class AbstractKey<K extends AbstractKey<K>> {
128         int hash;
129         int index = -1;
130         K next;
131 
132         abstract K dup();
133 
134         public abstract boolean equals(Object o);
135 
136         @Override
137         public int hashCode() {
138             return hash;
139         }
140 
141         void at(int index) {
142             this.index = index;
143         }
144     }
145 
146     public static class PoolKey extends AbstractKey<PoolKey> {
147         PoolTag tag;
148         Object o1;
149         Object o2;
150         Object o3;
151         Object o4;
152         int size = -1;
153 
154         void setUtf8(CharSequence s) {
155             tag = PoolTag.CONSTANT_UTF8;
156             o1 = s;
157             size = 1;
158             hash = tag.tag | (s.hashCode() << 1);
159         }
160 
161         void setClass(String clazz) {
162             tag = PoolTag.CONSTANT_CLASS;
163             o1 = clazz;
164             size = 1;
165             hash = tag.tag | (clazz.hashCode() << 1);
166         }
167 
168         void setNameAndType(CharSequence name, String type) {
169             tag = PoolTag.CONSTANT_NAMEANDTYPE;
170             o1 = name;
171             o2 = type;
172             size = 2;
173             hash = tag.tag | ((name.hashCode() | type.hashCode()) << 1);
174         }
175 
176         void setMemberRef(PoolTag poolTag, String owner, CharSequence name, String type) {
177             tag = poolTag;
178             o1 = owner;
179             o2 = name;
180             o3 = type;
181             size = 3;
182             hash = tag.tag | ((owner.hashCode() | name.hashCode() | type.hashCode()) << 1);
183         }
184 
185         void setInvokeDynamic(int bsmIndex, CharSequence name, String type) {
186             tag = PoolTag.CONSTANT_INVOKEDYNAMIC;
187             o1 = bsmIndex;
188             o2 = name;
189             o3 = type;
190             size = 3;
191             hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
192         }
193 
194         void setDynamicConstant(int bsmIndex, CharSequence name, String type) {
195             tag = PoolTag.CONSTANT_DYNAMIC;
196             o1 = bsmIndex;
197             o2 = name;
198             o3 = type;
199             size = 3;
200             hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
201         }
202 
203         void setString(String s) {
204             tag = PoolTag.CONSTANT_STRING;
205             o1 = s;
206             size = 1;
207             hash = tag.tag | (s.hashCode() << 1);
208         }
209 
210         void setInteger(Integer i) {
211             tag = PoolTag.CONSTANT_INTEGER;
212             o1 = i;
213             size = 1;
214             hash = tag.tag | (i.hashCode() << 1);
215         }
216 
217         void setFloat(Float f) {
218             tag = PoolTag.CONSTANT_FLOAT;
219             o1 = f;
220             size = 1;
221             hash = tag.tag | (f.hashCode() << 1);
222         }
223 
224         void setLong(Long l) {
225             tag = PoolTag.CONSTANT_LONG;
226             o1 = l;
227             size = 1;
228             hash = tag.tag | (l.hashCode() << 1);
229         }
230 
231         void setDouble(Double d) {
232             tag = PoolTag.CONSTANT_DOUBLE;
233             o1 = d;
234             size = 1;
235             hash = tag.tag | (d.hashCode() << 1);
236         }
237 
238         void setMethodType(String type) {
239             tag = PoolTag.CONSTANT_METHODTYPE;
240             o1 = type;
241             size = 1;
242             hash = tag.tag | (type.hashCode() << 1);
243         }
244 
245         void setMethodHandle(int bsmKind, String owner, CharSequence name, String type) {
246             tag = PoolTag.CONSTANT_METHODHANDLE;
247             o1 = bsmKind;
248             o2 = owner;
249             o3 = name;
250             o4 = type;
251             size = 4;
252             hash = tag.tag | (bsmKind | owner.hashCode() | name.hashCode() | type.hashCode() << 1);
253         }
254 
255         @Override
256         public boolean equals(Object obj) {
257             PoolKey that = (PoolKey) obj;
258             if (tag != that.tag) return false;
259             switch (size) {
260                 case 1:
261                     if (!o1.equals(that.o1)) {
262                         return false;
263                     }
264                     break;
265                 case 2:
266                     if (!o2.equals(that.o2) || !o1.equals(that.o1)) {
267                         return false;
268                     }
269                     break;
270                 case 3:
271                     if (!o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
272                         return false;
273                     }
274                     break;
275                 case 4:
276                     if (!o4.equals(that.o4) || !o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
277                         return false;
278                     }
279                     break;
280             }
281             return true;
282         }
283 
284         PoolKey dup() {
285             PoolKey poolKey = new PoolKey();
286             poolKey.tag = tag;
287             poolKey.size = size;
288             poolKey.hash = hash;
289             poolKey.o1 = o1;
290             poolKey.o2 = o2;
291             poolKey.o3 = o3;
292             poolKey.o4 = o4;
293             return poolKey;
294         }
295     }
296 
297     static class BsmKey extends AbstractKey<BsmKey> {
298         String bsmClass;
299         CharSequence bsmName;
300         String bsmType;
301         List<Integer> bsmArgs;
302 
303         void set(String bsmClass, CharSequence bsmName, String bsmType, List<Integer> bsmArgs) {
304             this.bsmClass = bsmClass;
305             this.bsmName = bsmName;
306             this.bsmType = bsmType;
307             this.bsmArgs = bsmArgs;
308             hash = bsmClass.hashCode() | bsmName.hashCode() | bsmType.hashCode() | Objects.hash(bsmArgs);
309         }
310 
311         BsmKey dup() {
312             BsmKey bsmKey = new BsmKey();
313             bsmKey.bsmClass = bsmClass;
314             bsmKey.bsmName = bsmName;
315             bsmKey.bsmType = bsmType;
316             bsmKey.bsmArgs = bsmArgs;
317             bsmKey.hash = hash;
318             return bsmKey;
319         }
320 
321         @Override
322         public boolean equals(Object obj) {
323             if (obj instanceof BsmKey) {
324                 BsmKey that = (BsmKey)obj;
325                 return Objects.equals(bsmClass, that.bsmClass) &&
326                         Objects.equals(bsmName, that.bsmName) &&
327                         Objects.equals(bsmType, that.bsmType) &&
328                         Objects.deepEquals(bsmArgs, that.bsmArgs);
329             } else {
330                 return false;
331             }
332         }
333     }
334 
335     @Override
336     public int putClass(S symbol) {
337         return putClassInternal(symbolToString.apply(symbol));
338     }
339 
340     @Override
341     public int putInlineClass(S symbol) {
342         return putClassInternal("Q" + symbolToString.apply(symbol) + ";");
343     }
344 
345     private int putClassInternal(String symbol) {
346         key.setClass(symbol);
347         PoolKey poolKey = entries.lookup(key);
348         if (poolKey == null) {
349             poolKey = key.dup();
350             int utf8_idx = putUtf8(symbol);
351             poolKey.at(currentIndex++);
352             entries.enter(poolKey);
353             pool.writeByte(PoolTag.CONSTANT_CLASS.tag);
354             pool.writeChar(utf8_idx);
355         }
356         return poolKey.index;
357     }
358 
359     @Override
360     public int putFieldRef(S owner, CharSequence name, T type) {
361         return putMemberRef(PoolTag.CONSTANT_FIELDREF, owner, name, type);
362     }
363 
364     @Override
365     public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
366         return putMemberRef(isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF,
367                 owner, name, type);
368     }
369 
370     int putMemberRef(PoolTag poolTag, S owner, CharSequence name, T type) {
371         return putMemberRefInternal(poolTag, symbolToString.apply(owner), name, typeToString.apply(type));
372     }
373 
374     int putMemberRefInternal(PoolTag poolTag, String owner, CharSequence name, String type) {
375         key.setMemberRef(poolTag, owner, name, type);
376         PoolKey poolKey = entries.lookup(key);
377         if (poolKey == null) {
378             poolKey = key.dup();
379             int owner_idx = putClassInternal(owner);
380             int nameAndType_idx = putNameAndType(name, type);
381             poolKey.at(currentIndex++);
382             entries.enter(poolKey);
383             pool.writeByte(poolTag.tag);
384             pool.writeChar(owner_idx);
385             pool.writeChar(nameAndType_idx);
386         }
387         return poolKey.index;
388     }
389 
390     @Override
391     public int putInt(int i) {
392         key.setInteger(i);
393         PoolKey poolKey = entries.lookup(key);
394         if (poolKey == null) {
395             poolKey = key.dup();
396             poolKey.at(currentIndex++);
397             entries.enter(poolKey);
398             pool.writeByte(PoolTag.CONSTANT_INTEGER.tag);
399             pool.writeInt(i);
400         }
401         return poolKey.index;
402     }
403 
404     @Override
405     public int putFloat(float f) {
406         key.setFloat(f);
407         PoolKey poolKey = entries.lookup(key);
408         if (poolKey == null) {
409             poolKey = key.dup();
410             poolKey.at(currentIndex++);
411             entries.enter(poolKey);
412             pool.writeByte(PoolTag.CONSTANT_FLOAT.tag);
413             pool.writeFloat(f);
414         }
415         return poolKey.index;
416     }
417 
418     @Override
419     public int putLong(long l) {
420         key.setLong(l);
421         PoolKey poolKey = entries.lookup(key);
422         if (poolKey == null) {
423             poolKey = key.dup();
424             poolKey.at(currentIndex++);
425             entries.enter(poolKey);
426             pool.writeByte(PoolTag.CONSTANT_LONG.tag);
427             pool.writeLong(l);
428             currentIndex++;
429         }
430         return poolKey.index;
431     }
432 
433     @Override
434     public int putDouble(double d) {
435         key.setDouble(d);
436         PoolKey poolKey = entries.lookup(key);
437         if (poolKey == null) {
438             poolKey = key.dup();
439             poolKey.at(currentIndex++);
440             entries.enter(poolKey);
441             pool.writeByte(PoolTag.CONSTANT_DOUBLE.tag);
442             pool.writeDouble(d);
443             currentIndex++;
444         }
445         return poolKey.index;
446     }
447 
448 
449     @Override
450     public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
451         return putInvokeDynamicInternal(invokedName, typeToString.apply(invokedType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
452     }
453 
454     @Override
455     public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
456         return putDynamicConstantInternal(constName, typeToString.apply(constType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
457     }
458 
459     private int putInvokeDynamicInternal(CharSequence invokedName, String invokedType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
460         int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
461         key.setInvokeDynamic(bsmIndex, invokedName, invokedType);
462         PoolKey poolKey = entries.lookup(key);
463         if (poolKey == null) {
464             poolKey = key.dup();
465             int nameAndType_idx = putNameAndType(invokedName, invokedType);
466             poolKey.at(currentIndex++);
467             entries.enter(poolKey);
468             pool.writeByte(PoolTag.CONSTANT_INVOKEDYNAMIC.tag);
469             pool.writeChar(bsmIndex);
470             pool.writeChar(nameAndType_idx);
471         }
472         return poolKey.index;
473     }
474 
475     private int putDynamicConstantInternal(CharSequence constName, String constType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
476         int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
477         key.setDynamicConstant(bsmIndex, constName, constType);
478         PoolKey poolKey = entries.lookup(key);
479         if (poolKey == null) {
480             poolKey = key.dup();
481             int nameAndType_idx = putNameAndType(constName, constType);
482             poolKey.at(currentIndex++);
483             entries.enter(poolKey);
484             pool.writeByte(PoolTag.CONSTANT_DYNAMIC.tag);
485             pool.writeChar(bsmIndex);
486             pool.writeChar(nameAndType_idx);
487         }
488         return poolKey.index;
489     }
490 
491     private int putBsmInternal(String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
492         ByteStaticArgListBuilder staticArgsBuilder = new ByteStaticArgListBuilder();
493         staticArgs.accept(staticArgsBuilder);
494         List<Integer> static_idxs = staticArgsBuilder.indexes;
495         bsmKey.set(bsmClass, bsmName, bsmType, static_idxs);
496         BsmKey poolKey = bootstraps.lookup(bsmKey);
497         if (poolKey == null) {
498             poolKey = bsmKey.dup();
499             // TODO the BSM could be a static method on an interface
500             int bsm_ref = putHandleInternal(MethodHandleInfo.REF_invokeStatic, bsmClass, bsmName, bsmType, false);
501             poolKey.at(currentBsmIndex++);
502             bootstraps.enter(poolKey);
503             bsm_attr.writeChar(bsm_ref);
504             bsm_attr.writeChar(static_idxs.size());
505             for (int i : static_idxs) {
506                 bsm_attr.writeChar(i);
507             }
508         }
509         return poolKey.index;
510     }
511     //where
512         class ByteStaticArgListBuilder implements StaticArgListBuilder<S, T, byte[]> {
513 
514             List<Integer> indexes = new ArrayList<>();
515 
516             public ByteStaticArgListBuilder add(int i) {
517                 indexes.add(putInt(i));
518                 return this;
519             }
520             public ByteStaticArgListBuilder add(float f) {
521                 indexes.add(putFloat(f));
522                 return this;
523             }
524             public ByteStaticArgListBuilder add(long l) {
525                 indexes.add(putLong(l));
526                 return this;
527             }
528             public ByteStaticArgListBuilder add(double d) {
529                 indexes.add(putDouble(d));
530                 return this;
531             }
532             public ByteStaticArgListBuilder add(String s) {
533                 indexes.add(putString(s));
534                 return this;
535             }
536             @Override
537             public StaticArgListBuilder<S, T, byte[]> add(int refKind, S owner, CharSequence name, T type) {
538                 indexes.add(putHandle(refKind, owner, name, type));
539                 return this;
540             }
541             public <Z> ByteStaticArgListBuilder add(Z z, ToIntBiFunction<PoolHelper<S, T, byte[]>, Z> poolFunc) {
542                 indexes.add(poolFunc.applyAsInt(BytePoolHelper.this, z));
543                 return this;
544             }
545             public ByteStaticArgListBuilder add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
546                 indexes.add(putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs));
547                 return this;
548             }
549         }
550 
551     @Override
552     public int putMethodType(T s) {
553         return putMethodTypeInternal(typeToString.apply(s));
554     }
555 
556     private int putMethodTypeInternal(String s) {
557         key.setMethodType(s);
558         PoolKey poolKey = entries.lookup(key);
559         if (poolKey == null) {
560             poolKey = key.dup();
561             int desc_idx = putUtf8(s);
562             poolKey.at(currentIndex++);
563             entries.enter(poolKey);
564             pool.writeByte(PoolTag.CONSTANT_METHODTYPE.tag);
565             pool.writeChar(desc_idx);
566         }
567         return poolKey.index;
568     }
569 
570     @Override
571     public int putHandle(int refKind, S owner, CharSequence name, T type) {
572         return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type), false);
573     }
574 
575     @Override
576     public int putHandle(int refKind, S owner, CharSequence name, T type, boolean isInterface) {
577         return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type), isInterface);
578     }
579 
580     private int putHandleInternal(int refKind, String owner, CharSequence name, String type, boolean isInterface) {
581         key.setMethodHandle(refKind, owner, name, type);
582         PoolKey poolKey = entries.lookup(key);
583         if (poolKey == null) {
584             poolKey = key.dup();
585             int ref_idx = putMemberRefInternal(fromKind(refKind, isInterface), owner, name, type);
586             poolKey.at(currentIndex++);
587             entries.enter(poolKey);
588             pool.writeByte(PoolTag.CONSTANT_METHODHANDLE.tag);
589             pool.writeByte(refKind);
590             pool.writeChar(ref_idx);
591         }
592         return poolKey.index;
593     }
594 
595     PoolTag fromKind(int bsmKind, boolean isInterface) {
596         switch (bsmKind) {
597             case 1: // REF_getField
598             case 2: // REF_getStatic
599             case 3: // REF_putField
600             case 4: // REF_putStatic
601                 return PoolTag.CONSTANT_FIELDREF;
602             case 5: // REF_invokeVirtual
603             case 6: // REF_invokeStatic
604             case 7: // REF_invokeSpecial
605             case 8: // REF_newInvokeSpecial
606             case 9: // REF_invokeInterface
607                 return isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF;
608             default:
609                 throw new IllegalStateException();
610         }
611     }
612 
613     @Override
614     public int putType(T s) {
615         return putUtf8(typeToString.apply(s));
616     }
617 
618     public int putUtf8(CharSequence s) {
619         key.setUtf8(s);
620         PoolKey poolKey = entries.lookup(key);
621         if (poolKey == null) {
622             poolKey = key.dup();
623             poolKey.at(currentIndex++);
624             entries.enter(poolKey);
625             pool.writeByte(PoolTag.CONSTANT_UTF8.tag);
626             putUTF8Internal(s);
627         }
628         return poolKey.index;
629     }
630 
631     /**
632      * Puts an UTF8 string into this byte vector. The byte vector is
633      * automatically enlarged if necessary.
634      *
635      * @param s a String whose UTF8 encoded length must be less than 65536.
636      * @return this byte vector.
637      */
638     void putUTF8Internal(final CharSequence s) {
639         int charLength = s.length();
640         if (charLength > 65535) {
641             throw new IllegalArgumentException();
642         }
643         // optimistic algorithm: instead of computing the byte length and then
644         // serializing the string (which requires two loops), we assume the byte
645         // length is equal to char length (which is the most frequent case), and
646         // we start serializing the string right away. During the serialization,
647         // if we find that this assumption is wrong, we continue with the
648         // general method.
649         pool.writeChar(charLength);
650         for (int i = 0; i < charLength; ++i) {
651             char c = s.charAt(i);
652             if (c >= '\001' && c <= '\177') {
653                 pool.writeByte((byte) c);
654             } else {
655                 encodeUTF8(s, i, 65535);
656                 break;
657             }
658         }
659     }
660 
661     /**
662      * Puts an UTF8 string into this byte vector. The byte vector is
663      * automatically enlarged if necessary. The string length is encoded in two
664      * bytes before the encoded characters, if there is space for that (i.e. if
665      * this.length - i - 2 >= 0).
666      *
667      * @param s             the String to encode.
668      * @param i             the index of the first character to encode. The previous
669      *                      characters are supposed to have already been encoded, using
670      *                      only one byte per character.
671      * @param maxByteLength the maximum byte length of the encoded string, including the
672      *                      already encoded characters.
673      * @return this byte vector.
674      */
675     void encodeUTF8(final CharSequence s, int i, int maxByteLength) {
676         int charLength = s.length();
677         int byteLength = i;
678         char c;
679         for (int j = i; j < charLength; ++j) {
680             c = s.charAt(j);
681             if (c >= '\001' && c <= '\177') {
682                 byteLength++;
683             } else if (c > '\u07FF') {
684                 byteLength += 3;
685             } else {
686                 byteLength += 2;
687             }
688         }
689         if (byteLength > maxByteLength) {
690             throw new IllegalArgumentException();
691         }
692         int byteLengthFinal = byteLength;
693         pool.withOffset(pool.offset - i - 2, buf -> buf.writeChar(byteLengthFinal));
694         for (int j = i; j < charLength; ++j) {
695             c = s.charAt(j);
696             if (c >= '\001' && c <= '\177') {
697                 pool.writeChar((byte) c);
698             } else if (c > '\u07FF') {
699                 pool.writeChar((byte) (0xE0 | c >> 12 & 0xF));
700                 pool.writeChar((byte) (0x80 | c >> 6 & 0x3F));
701                 pool.writeChar((byte) (0x80 | c & 0x3F));
702             } else {
703                 pool.writeChar((byte) (0xC0 | c >> 6 & 0x1F));
704                 pool.writeChar((byte) (0x80 | c & 0x3F));
705             }
706         }
707     }
708 
709     @Override
710     public int putString(String s) {
711         key.setString(s);
712         PoolKey poolKey = entries.lookup(key);
713         if (poolKey == null) {
714             poolKey = key.dup();
715             int utf8_index = putUtf8(s);
716             poolKey.at(currentIndex++);
717             entries.enter(poolKey);
718             pool.writeByte(PoolTag.CONSTANT_STRING.tag);
719             pool.writeChar(utf8_index);
720         }
721         return poolKey.index;
722     }
723 
724     int putNameAndType(CharSequence name, String type) {
725         key.setNameAndType(name, type);
726         PoolKey poolKey = entries.lookup(key);
727         if (poolKey == null) {
728             poolKey = key.dup();
729             int name_idx = putUtf8(name);
730             int type_idx = putUtf8(type);
731             poolKey.at(currentIndex++);
732             entries.enter(poolKey);
733             pool.writeByte(PoolTag.CONSTANT_NAMEANDTYPE.tag);
734             pool.writeChar(name_idx);
735             pool.writeChar(type_idx);
736         }
737         return poolKey.index;
738     }
739 
740     @Override
741     public int size() {
742         return currentIndex - 1;
743     }
744 
745     @Override
746     public byte[] entries() {
747         return pool.bytes();
748     }
749 
750     <Z extends ClassBuilder<S, T, Z>> void addAttributes(ClassBuilder<S , T, Z> cb) {
751         if (currentBsmIndex > 0) {
752             GrowableByteBuffer bsmAttrBuf = new GrowableByteBuffer();
753             bsmAttrBuf.writeChar(currentBsmIndex);
754             bsmAttrBuf.writeBytes(bsm_attr);
755             cb.withAttribute("BootstrapMethods", bsmAttrBuf.bytes());
756         }
757     }
758 }