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             int bsm_ref = putHandleInternal(MethodHandleInfo.REF_invokeStatic, bsmClass, bsmName, bsmType);
 494             poolKey.at(currentBsmIndex++);
 495             bootstraps.enter(poolKey);
 496             bsm_attr.writeChar(bsm_ref);
 497             bsm_attr.writeChar(static_idxs.size());
 498             for (int i : static_idxs) {
 499                 bsm_attr.writeChar(i);
 500             }
 501         }
 502         return poolKey.index;
 503     }
 504     //where
 505         class ByteStaticArgListBuilder implements StaticArgListBuilder<S, T, byte[]> {
 506 
 507             List<Integer> indexes = new ArrayList<>();
 508 
 509             public ByteStaticArgListBuilder add(int i) {
 510                 indexes.add(putInt(i));
 511                 return this;
 512             }
 513             public ByteStaticArgListBuilder add(float f) {
 514                 indexes.add(putFloat(f));
 515                 return this;
 516             }
 517             public ByteStaticArgListBuilder add(long l) {
 518                 indexes.add(putLong(l));
 519                 return this;
 520             }
 521             public ByteStaticArgListBuilder add(double d) {
 522                 indexes.add(putDouble(d));
 523                 return this;
 524             }
 525             public ByteStaticArgListBuilder add(String s) {
 526                 indexes.add(putString(s));
 527                 return this;
 528             }
 529             @Override
 530             public StaticArgListBuilder<S, T, byte[]> add(int refKind, S owner, CharSequence name, T type) {
 531                 indexes.add(putHandle(refKind, owner, name, type));
 532                 return this;
 533             }
 534             public <Z> ByteStaticArgListBuilder add(Z z, ToIntBiFunction<PoolHelper<S, T, byte[]>, Z> poolFunc) {
 535                 indexes.add(poolFunc.applyAsInt(BytePoolHelper.this, z));
 536                 return this;
 537             }
 538             public ByteStaticArgListBuilder add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
 539                 indexes.add(putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs));
 540                 return this;
 541             }
 542         }
 543 
 544     @Override
 545     public int putMethodType(T s) {
 546         return putMethodTypeInternal(typeToString.apply(s));
 547     }
 548 
 549     private int putMethodTypeInternal(String s) {
 550         key.setMethodType(s);
 551         PoolKey poolKey = entries.lookup(key);
 552         if (poolKey == null) {
 553             poolKey = key.dup();
 554             int desc_idx = putUtf8(s);
 555             poolKey.at(currentIndex++);
 556             entries.enter(poolKey);
 557             pool.writeByte(PoolTag.CONSTANT_METHODTYPE.tag);
 558             pool.writeChar(desc_idx);
 559         }
 560         return poolKey.index;
 561     }
 562 
 563     @Override
 564     public int putHandle(int refKind, S owner, CharSequence name, T type) {
 565         return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type));
 566     }
 567 
 568     private int putHandleInternal(int refKind, String owner, CharSequence name, String type) {
 569         key.setMethodHandle(refKind, owner, name, type);
 570         PoolKey poolKey = entries.lookup(key);
 571         if (poolKey == null) {
 572             poolKey = key.dup();
 573             int ref_idx = putMemberRefInternal(fromKind(refKind), owner, name, type);
 574             poolKey.at(currentIndex++);
 575             entries.enter(poolKey);
 576             pool.writeByte(PoolTag.CONSTANT_METHODHANDLE.tag);
 577             pool.writeByte(refKind);
 578             pool.writeChar(ref_idx);
 579         }
 580         return poolKey.index;
 581     }
 582 
 583     PoolTag fromKind(int bsmKind) {
 584         switch (bsmKind) {
 585             case 1: // REF_getField
 586             case 2: // REF_getStatic
 587             case 3: // REF_putField
 588             case 4: // REF_putStatic
 589                 return PoolTag.CONSTANT_FIELDREF;
 590             case 5: // REF_invokeVirtual
 591             case 6: // REF_invokeStatic
 592             case 7: // REF_invokeSpecial
 593             case 8: // REF_newInvokeSpecial
 594                 return PoolTag.CONSTANT_METHODREF;
 595             case 9: // REF_invokeInterface
 596                 return PoolTag.CONSTANT_INTERFACEMETHODREF;
 597             default:
 598                 throw new IllegalStateException();
 599         }
 600     }
 601 
 602     @Override
 603     public int putType(T s) {
 604         return putUtf8(typeToString.apply(s));
 605     }
 606 
 607     public int putUtf8(CharSequence s) {
 608         key.setUtf8(s);
 609         PoolKey poolKey = entries.lookup(key);
 610         if (poolKey == null) {
 611             poolKey = key.dup();
 612             poolKey.at(currentIndex++);
 613             entries.enter(poolKey);
 614             pool.writeByte(PoolTag.CONSTANT_UTF8.tag);
 615             putUTF8Internal(s);
 616         }
 617         return poolKey.index;
 618     }
 619 
 620     /**
 621      * Puts an UTF8 string into this byte vector. The byte vector is
 622      * automatically enlarged if necessary.
 623      *
 624      * @param s a String whose UTF8 encoded length must be less than 65536.
 625      * @return this byte vector.
 626      */
 627     void putUTF8Internal(final CharSequence s) {
 628         int charLength = s.length();
 629         if (charLength > 65535) {
 630             throw new IllegalArgumentException();
 631         }
 632         // optimistic algorithm: instead of computing the byte length and then
 633         // serializing the string (which requires two loops), we assume the byte
 634         // length is equal to char length (which is the most frequent case), and
 635         // we start serializing the string right away. During the serialization,
 636         // if we find that this assumption is wrong, we continue with the
 637         // general method.
 638         pool.writeChar(charLength);
 639         for (int i = 0; i < charLength; ++i) {
 640             char c = s.charAt(i);
 641             if (c >= '\001' && c <= '\177') {
 642                 pool.writeByte((byte) c);
 643             } else {
 644                 encodeUTF8(s, i, 65535);
 645                 break;
 646             }
 647         }
 648     }
 649 
 650     /**
 651      * Puts an UTF8 string into this byte vector. The byte vector is
 652      * automatically enlarged if necessary. The string length is encoded in two
 653      * bytes before the encoded characters, if there is space for that (i.e. if
 654      * this.length - i - 2 >= 0).
 655      *
 656      * @param s             the String to encode.
 657      * @param i             the index of the first character to encode. The previous
 658      *                      characters are supposed to have already been encoded, using
 659      *                      only one byte per character.
 660      * @param maxByteLength the maximum byte length of the encoded string, including the
 661      *                      already encoded characters.
 662      * @return this byte vector.
 663      */
 664     void encodeUTF8(final CharSequence s, int i, int maxByteLength) {
 665         int charLength = s.length();
 666         int byteLength = i;
 667         char c;
 668         for (int j = i; j < charLength; ++j) {
 669             c = s.charAt(j);
 670             if (c >= '\001' && c <= '\177') {
 671                 byteLength++;
 672             } else if (c > '\u07FF') {
 673                 byteLength += 3;
 674             } else {
 675                 byteLength += 2;
 676             }
 677         }
 678         if (byteLength > maxByteLength) {
 679             throw new IllegalArgumentException();
 680         }
 681         int byteLengthFinal = byteLength;
 682         pool.withOffset(pool.offset - i - 2, buf -> buf.writeChar(byteLengthFinal));
 683         for (int j = i; j < charLength; ++j) {
 684             c = s.charAt(j);
 685             if (c >= '\001' && c <= '\177') {
 686                 pool.writeChar((byte) c);
 687             } else if (c > '\u07FF') {
 688                 pool.writeChar((byte) (0xE0 | c >> 12 & 0xF));
 689                 pool.writeChar((byte) (0x80 | c >> 6 & 0x3F));
 690                 pool.writeChar((byte) (0x80 | c & 0x3F));
 691             } else {
 692                 pool.writeChar((byte) (0xC0 | c >> 6 & 0x1F));
 693                 pool.writeChar((byte) (0x80 | c & 0x3F));
 694             }
 695         }
 696     }
 697 
 698     @Override
 699     public int putString(String s) {
 700         key.setString(s);
 701         PoolKey poolKey = entries.lookup(key);
 702         if (poolKey == null) {
 703             poolKey = key.dup();
 704             int utf8_index = putUtf8(s);
 705             poolKey.at(currentIndex++);
 706             entries.enter(poolKey);
 707             pool.writeByte(PoolTag.CONSTANT_STRING.tag);
 708             pool.writeChar(utf8_index);
 709         }
 710         return poolKey.index;
 711     }
 712 
 713     int putNameAndType(CharSequence name, String type) {
 714         key.setNameAndType(name, type);
 715         PoolKey poolKey = entries.lookup(key);
 716         if (poolKey == null) {
 717             poolKey = key.dup();
 718             int name_idx = putUtf8(name);
 719             int type_idx = putUtf8(type);
 720             poolKey.at(currentIndex++);
 721             entries.enter(poolKey);
 722             pool.writeByte(PoolTag.CONSTANT_NAMEANDTYPE.tag);
 723             pool.writeChar(name_idx);
 724             pool.writeChar(type_idx);
 725         }
 726         return poolKey.index;
 727     }
 728 
 729     @Override
 730     public int size() {
 731         return currentIndex - 1;
 732     }
 733 
 734     @Override
 735     public byte[] entries() {
 736         return pool.bytes();
 737     }
 738 
 739     <Z extends ClassBuilder<S, T, Z>> void addAttributes(ClassBuilder<S , T, Z> cb) {
 740         if (currentBsmIndex > 0) {
 741             GrowableByteBuffer bsmAttrBuf = new GrowableByteBuffer();
 742             bsmAttrBuf.writeChar(currentBsmIndex);
 743             bsmAttrBuf.writeBytes(bsm_attr);
 744             cb.withAttribute("BootstrapMethods", bsmAttrBuf.bytes());
 745         }
 746     }
 747 }