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