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 }