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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.experimental.bytecode;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodHandleInfo;
  30 import java.lang.invoke.MethodHandles;
  31 import java.lang.invoke.MethodType;
  32 import java.lang.reflect.Constructor;
  33 import java.lang.reflect.Field;
  34 import java.lang.reflect.Member;
  35 import java.lang.reflect.Method;
  36 import java.util.ArrayList;
  37 import java.util.List;
  38 import java.util.Objects;
  39 import java.util.function.Consumer;
  40 import java.util.function.Function;
  41 import java.util.function.ToIntBiFunction;
  42 
  43 /**
  44  * A helper for building and tracking constant pools whose entries are
  45  * represented as byte arrays.
  46  *
  47  * @param <S> the type of the symbol representation
  48  * @param <T> the type of type descriptors representation
  49  */
  50 public class BytePoolHelper<S, T> implements PoolHelper<S, T, byte[]> {
  51 
  52     final BytePoolBuilder builder;
  53     GrowableByteBuffer bsm_attr = new GrowableByteBuffer();
  54     //Map<PoolKey, PoolKey> indicesMap = new HashMap<>();
  55     int currentBsmIndex = 0;
  56 
  57     KeyMap<PoolKey> entries = new KeyMap<>();
  58     KeyMap<BsmKey> bootstraps = new KeyMap<>();
  59     PoolKey key = new PoolKey();
  60     BsmKey bsmKey = new BsmKey();
  61 
  62     Function<S, String> symbolToString;
  63     Function<T, String> typeToString;
  64 
  65     public BytePoolHelper(Function<S, String> symbolToString, Function<T, String> typeToString) {
  66         this(new BytePoolBuilder(), symbolToString, typeToString);
  67     }
  68 
  69     BytePoolHelper(BytePoolBuilder builder, Function<S, String> symbolToString, Function<T, String> typeToString) {
  70         this.builder = builder;
  71         this.symbolToString = symbolToString;
  72         this.typeToString = typeToString;
  73     }
  74     
  75     public static <S, T> BytePoolHelper<S, T> immutable(Consumer<PoolBuilder<byte[]>> cpBuild,
  76             Function<S, String> symbolToString, Function<T, String> typeToString) {
  77         return new ImmutableBytePoolHelper<>(cpBuild, symbolToString, typeToString);
  78     }
  79 
  80     static class KeyMap<K extends AbstractKey<K>> {
  81 
  82         @SuppressWarnings("unchecked")
  83         K[] table = (K[])new AbstractKey<?>[0x10];
  84         int nelems;
  85 
  86         public void enter(K e) {
  87             if (nelems * 3 >= (table.length - 1) * 2)
  88                 dble();
  89             int hash = getIndex(e);
  90             K old = table[hash];
  91             if (old == null) {
  92                 nelems++;
  93             }
  94             e.next = old;
  95             table[hash] = e;
  96         }
  97 
  98         protected K lookup(K other) {
  99             K e = table[getIndex(other)];
 100             while (e != null && !e.equals(other))
 101                 e = e.next;
 102             return e;
 103         }
 104 
 105         /**
 106          * Look for slot in the table.
 107          * We use open addressing with double hashing.
 108          */
 109         int getIndex(K e) {
 110             int hashMask = table.length - 1;
 111             int h = e.hashCode();
 112             int i = h & hashMask;
 113             // The expression below is always odd, so it is guaranteed
 114             // to be mutually prime with table.length, a power of 2.
 115             int x = hashMask - ((h + (h >> 16)) << 1);
 116             for (; ; ) {
 117                 K e2 = table[i];
 118                 if (e2 == null)
 119                     return i;
 120                 else if (e.hash == e2.hash)
 121                     return i;
 122                 i = (i + x) & hashMask;
 123             }
 124         }
 125 
 126         @SuppressWarnings("unchecked")
 127         private void dble() {
 128             K[] oldtable = table;
 129             table = (K[])new AbstractKey<?>[oldtable.length * 2];
 130             int n = 0;
 131             for (int i = oldtable.length; --i >= 0; ) {
 132                 K e = oldtable[i];
 133                 if (e != null) {
 134                     table[getIndex(e)] = e;
 135                     n++;
 136                 }
 137             }
 138             // We don't need to update nelems for shared inherited scopes,
 139             // since that gets handled by leave().
 140             nelems = n;
 141         }
 142     }
 143 
 144     public static abstract class AbstractKey<K extends AbstractKey<K>> {
 145         int hash;
 146         int index = -1;
 147         K next;
 148 
 149         abstract K dup();
 150 
 151         @Override
 152         public abstract boolean equals(Object o);
 153 
 154         @Override
 155         public int hashCode() {
 156             return hash;
 157         }
 158 
 159         void at(int index) {
 160             this.index = index;
 161         }
 162     }
 163 
 164     public static class PoolKey extends AbstractKey<PoolKey> {
 165         PoolTag tag;
 166         Object o1;
 167         Object o2;
 168         Object o3;
 169         Object o4;
 170         int size = -1;
 171 
 172         void setUtf8(CharSequence s) {
 173             tag = PoolTag.UTF8;
 174             o1 = s;
 175             size = 1;
 176             hash = tag.tag | (s.hashCode() << 1);
 177         }
 178 
 179         void setClass(String clazz) {
 180             tag = PoolTag.CLASS;
 181             o1 = clazz;
 182             size = 1;
 183             hash = tag.tag | (clazz.hashCode() << 1);
 184         }
 185 
 186         void setNameAndType(CharSequence name, String type) {
 187             tag = PoolTag.NAMEANDTYPE;
 188             o1 = name;
 189             o2 = type;
 190             size = 2;
 191             hash = tag.tag | ((name.hashCode() | type.hashCode()) << 1);
 192         }
 193 
 194         void setMemberRef(PoolTag poolTag, String owner, CharSequence name, String type) {
 195             tag = poolTag;
 196             o1 = owner;
 197             o2 = name;
 198             o3 = type;
 199             size = 3;
 200             hash = tag.tag | ((owner.hashCode() | name.hashCode() | type.hashCode()) << 1);
 201         }
 202 
 203         void setInvokeDynamic(int bsmIndex, CharSequence name, String type) {
 204             tag = PoolTag.INVOKEDYNAMIC;
 205             o1 = bsmIndex;
 206             o2 = name;
 207             o3 = type;
 208             size = 3;
 209             hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
 210         }
 211 
 212         void setConstantDynamic(int bsmIndex, CharSequence name, String type) {
 213             tag = PoolTag.INVOKEDYNAMIC;
 214             o1 = bsmIndex;
 215             o2 = name;
 216             o3 = type;
 217             size = 3;
 218             hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
 219         }
 220 
 221         void setString(String s) {
 222             tag = PoolTag.STRING;
 223             o1 = s;
 224             size = 1;
 225             hash = tag.tag | (s.hashCode() << 1);
 226         }
 227 
 228         void setInteger(Integer i) {
 229             tag = PoolTag.INTEGER;
 230             o1 = i;
 231             size = 1;
 232             hash = tag.tag | (i.hashCode() << 1);
 233         }
 234 
 235         void setFloat(Float f) {
 236             tag = PoolTag.FLOAT;
 237             o1 = f;
 238             size = 1;
 239             hash = tag.tag | (f.hashCode() << 1);
 240         }
 241 
 242         void setLong(Long l) {
 243             tag = PoolTag.LONG;
 244             o1 = l;
 245             size = 1;
 246             hash = tag.tag | (l.hashCode() << 1);
 247         }
 248 
 249         void setDouble(Double d) {
 250             tag = PoolTag.DOUBLE;
 251             o1 = d;
 252             size = 1;
 253             hash = tag.tag | (d.hashCode() << 1);
 254         }
 255 
 256         void setMethodType(String type) {
 257             tag = PoolTag.METHODTYPE;
 258             o1 = type;
 259             size = 1;
 260             hash = tag.tag | (type.hashCode() << 1);
 261         }
 262 
 263         void setMethodType(MethodType mt) {
 264             tag = PoolTag.METHODTYPE;
 265             o1 = mt;
 266             size = 1;
 267             hash = tag.tag | (mt.hashCode() << 1);
 268         }
 269 
 270         void setMethodHandle(int bsmKind, String owner, CharSequence name, String type) {
 271             tag = PoolTag.METHODHANDLE;
 272             o1 = bsmKind;
 273             o2 = owner;
 274             o3 = name;
 275             o4 = type;
 276             size = 4;
 277             hash = tag.tag | (bsmKind | owner.hashCode() | name.hashCode() | type.hashCode() << 1);
 278         }
 279 
 280         void setMethodHandle(MethodHandle mh) {
 281             tag = PoolTag.METHODHANDLE;
 282             o1 = mh;
 283             size = 1;
 284             hash = tag.tag | (mh.hashCode() << 1);
 285         }
 286 
 287         @Override
 288         public boolean equals(Object obj) {
 289             PoolKey that = (PoolKey) obj;
 290             if (tag != that.tag) return false;
 291             switch (size) {
 292                 case 1:
 293                     if (!o1.equals(that.o1)) {
 294                         return false;
 295                     }
 296                     break;
 297                 case 2:
 298                     if (!o2.equals(that.o2) || !o1.equals(that.o1)) {
 299                         return false;
 300                     }
 301                     break;
 302                 case 3:
 303                     if (!o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
 304                         return false;
 305                     }
 306                     break;
 307                 case 4:
 308                     if (!o4.equals(that.o4) || !o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
 309                         return false;
 310                     }
 311                     break;
 312             }
 313             return true;
 314         }
 315 
 316         @Override
 317         PoolKey dup() {
 318             PoolKey poolKey = new PoolKey();
 319             poolKey.tag = tag;
 320             poolKey.size = size;
 321             poolKey.hash = hash;
 322             poolKey.o1 = o1;
 323             poolKey.o2 = o2;
 324             poolKey.o3 = o3;
 325             poolKey.o4 = o4;
 326             return poolKey;
 327         }
 328     }
 329 
 330     static class BsmKey extends AbstractKey<BsmKey> {
 331         String bsmClass;
 332         CharSequence bsmName;
 333         String bsmType;
 334         List<Integer> bsmArgs;
 335 
 336         void set(String bsmClass, CharSequence bsmName, String bsmType, List<Integer> bsmArgs) {
 337             this.bsmClass = bsmClass;
 338             this.bsmName = bsmName;
 339             this.bsmType = bsmType;
 340             this.bsmArgs = bsmArgs;
 341             hash = bsmClass.hashCode() | bsmName.hashCode() | bsmType.hashCode() | Objects.hash(bsmArgs);
 342         }
 343 
 344         @Override
 345         BsmKey dup() {
 346             BsmKey bsmKey = new BsmKey();
 347             bsmKey.bsmClass = bsmClass;
 348             bsmKey.bsmName = bsmName;
 349             bsmKey.bsmType = bsmType;
 350             bsmKey.bsmArgs = bsmArgs;
 351             bsmKey.hash = hash;
 352             return bsmKey;
 353         }
 354 
 355         //TODO: missing hashCode()
 356 
 357         @Override
 358         public boolean equals(Object obj) {
 359             if (obj instanceof BsmKey) {
 360                 BsmKey that = (BsmKey)obj;
 361                 return Objects.equals(bsmClass, that.bsmClass) &&
 362                         Objects.equals(bsmName, that.bsmName) &&
 363                         Objects.equals(bsmType, that.bsmType) &&
 364                         Objects.deepEquals(bsmArgs, that.bsmArgs);
 365             } else {
 366                 return false;
 367             }
 368         }
 369     }
 370 
 371     @Override
 372     public int putClass(S symbol) {
 373         return putClassInternal(symbolToString.apply(symbol));
 374     }
 375 
 376     @Override
 377     public int putValueClass(S symbol) {
 378         return putClassInternal("Q" + symbolToString.apply(symbol) + ";");
 379     }
 380 
 381     private int putClassInternal(String symbol) {
 382         key.setClass(symbol);
 383         PoolKey poolKey = entries.lookup(key);
 384         if (poolKey == null) {
 385             poolKey = key.dup();
 386             int utf8_idx = putUtf8(symbol);
 387             poolKey.at(builder.putClass(utf8_idx));
 388             entries.enter(poolKey);
 389         }
 390         return poolKey.index;
 391     }
 392 
 393     @Override
 394     public int putFieldRef(S owner, CharSequence name, T type) {
 395         return putMemberRef(PoolTag.FIELDREF, owner, name, type);
 396     }
 397 
 398     @Override
 399     public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
 400         return putMemberRef(isInterface ? PoolTag.INTERFACEMETHODREF : PoolTag.METHODREF,
 401                 owner, name, type);
 402     }
 403 
 404     int putMemberRef(PoolTag poolTag, S owner, CharSequence name, T type) {
 405         return putMemberRefInternal(poolTag, symbolToString.apply(owner), name, typeToString.apply(type));
 406     }
 407 
 408     int putMemberRefInternal(PoolTag poolTag, String owner, CharSequence name, String type) {
 409         key.setMemberRef(poolTag, owner, name, type);
 410         PoolKey poolKey = entries.lookup(key);
 411         if (poolKey == null) {
 412             poolKey = key.dup();
 413             int owner_idx = putClassInternal(owner);
 414             int nameAndType_idx = putNameAndType(name, type);
 415             poolKey.at(builder.putMemberRef(poolTag, owner_idx, nameAndType_idx));
 416             entries.enter(poolKey);
 417         }
 418         return poolKey.index;
 419     }
 420 
 421     @Override
 422     public int putInt(int i) {
 423         key.setInteger(i);
 424         PoolKey poolKey = entries.lookup(key);
 425         if (poolKey == null) {
 426             poolKey = key.dup();
 427             poolKey.at(builder.putInt(i));
 428             entries.enter(poolKey);
 429         }
 430         return poolKey.index;
 431     }
 432 
 433     @Override
 434     public int putFloat(float f) {
 435         key.setFloat(f);
 436         PoolKey poolKey = entries.lookup(key);
 437         if (poolKey == null) {
 438             poolKey = key.dup();
 439             poolKey.at(builder.putFloat(f));
 440             entries.enter(poolKey);
 441         }
 442         return poolKey.index;
 443     }
 444 
 445     @Override
 446     public int putLong(long l) {
 447         key.setLong(l);
 448         PoolKey poolKey = entries.lookup(key);
 449         if (poolKey == null) {
 450             poolKey = key.dup();
 451             poolKey.at(builder.putLong(l));
 452             entries.enter(poolKey);
 453         }
 454         return poolKey.index;
 455     }
 456 
 457     @Override
 458     public int putDouble(double d) {
 459         key.setDouble(d);
 460         PoolKey poolKey = entries.lookup(key);
 461         if (poolKey == null) {
 462             poolKey = key.dup();
 463             poolKey.at(builder.putDouble(d));
 464             entries.enter(poolKey);
 465         }
 466         return poolKey.index;
 467     }
 468 
 469 
 470     @Override
 471     public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
 472         return putInvokeDynamicInternal(invokedName, typeToString.apply(invokedType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
 473     }
 474 
 475     @Override
 476     public int putConstantDynamic(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
 477         return putConstantDynamicInternal(constName, typeToString.apply(constType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
 478     }
 479 
 480     private int putInvokeDynamicInternal(CharSequence invokedName, String invokedType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
 481         int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
 482         key.setInvokeDynamic(bsmIndex, invokedName, invokedType);
 483         PoolKey poolKey = entries.lookup(key);
 484         if (poolKey == null) {
 485             poolKey = key.dup();
 486             int nameAndType_idx = putNameAndType(invokedName, invokedType);
 487             poolKey.at(builder.putInvokeDynamic(bsmIndex, nameAndType_idx));
 488             entries.enter(poolKey);
 489         }
 490         return poolKey.index;
 491     }
 492 
 493     private int putConstantDynamicInternal(CharSequence constName, String constType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
 494         int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
 495         key.setConstantDynamic(bsmIndex, constName, constType);
 496         PoolKey poolKey = entries.lookup(key);
 497         if (poolKey == null) {
 498             poolKey = key.dup();
 499             int nameAndType_idx = putNameAndType(constName, constType);
 500             poolKey.at(builder.putConstantDynamic(bsmIndex, nameAndType_idx));
 501             entries.enter(poolKey);
 502         }
 503         return poolKey.index;
 504     }
 505 
 506     private int putBsmInternal(String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
 507         ByteStaticArgListBuilder staticArgsBuilder = new ByteStaticArgListBuilder();
 508         staticArgs.accept(staticArgsBuilder);
 509         List<Integer> static_idxs = staticArgsBuilder.indexes;
 510         bsmKey.set(bsmClass, bsmName, bsmType, static_idxs);
 511         BsmKey poolKey = bootstraps.lookup(bsmKey);
 512         if (poolKey == null) {
 513             poolKey = bsmKey.dup();
 514             int bsm_ref = putMethodHandleInternal(MethodHandleInfo.REF_invokeStatic, bsmClass, bsmName, bsmType, false);
 515             poolKey.at(currentBsmIndex++);
 516             bootstraps.enter(poolKey);
 517             bsm_attr.writeChar(bsm_ref);
 518             bsm_attr.writeChar(static_idxs.size());
 519             for (int i : static_idxs) {
 520                 bsm_attr.writeChar(i);
 521             }
 522         }
 523         return poolKey.index;
 524     }
 525     //where
 526         class ByteStaticArgListBuilder implements StaticArgListBuilder<S, T, byte[]> {
 527 
 528             List<Integer> indexes = new ArrayList<>();
 529 
 530             @Override
 531             public ByteStaticArgListBuilder add(int i) {
 532                 indexes.add(putInt(i));
 533                 return this;
 534             }
 535             @Override
 536             public ByteStaticArgListBuilder add(float f) {
 537                 indexes.add(putFloat(f));
 538                 return this;
 539             }
 540             @Override
 541             public ByteStaticArgListBuilder add(long l) {
 542                 indexes.add(putLong(l));
 543                 return this;
 544             }
 545             @Override
 546             public ByteStaticArgListBuilder add(double d) {
 547                 indexes.add(putDouble(d));
 548                 return this;
 549             }
 550             @Override
 551             public ByteStaticArgListBuilder add(String s) {
 552                 indexes.add(putString(s));
 553                 return this;
 554             }
 555             @Override
 556             public StaticArgListBuilder<S, T, byte[]> add(int refKind, S owner, CharSequence name, T type) {
 557                 indexes.add(putMethodHandle(refKind, owner, name, type));
 558                 return this;
 559             }
 560             @Override
 561             public <Z> ByteStaticArgListBuilder add(Z z, ToIntBiFunction<PoolHelper<S, T, byte[]>, Z> poolFunc) {
 562                 indexes.add(poolFunc.applyAsInt(BytePoolHelper.this, z));
 563                 return this;
 564             }
 565             @Override
 566             public ByteStaticArgListBuilder add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
 567                 indexes.add(putConstantDynamic(constName, constType, bsmClass, bsmName, bsmType, staticArgs));
 568                 return this;
 569             }
 570         }
 571 
 572     @Override
 573     public int putMethodType(T s) {
 574         return putMethodTypeInternal(typeToString.apply(s));
 575     }
 576 
 577     private int putMethodTypeInternal(String s) {
 578         key.setMethodType(s);
 579         PoolKey poolKey = entries.lookup(key);
 580         if (poolKey == null) {
 581             poolKey = key.dup();
 582             int desc_idx = putUtf8(s);
 583             poolKey.at(builder.putMethodType(desc_idx));
 584             entries.enter(poolKey);
 585         }
 586         return poolKey.index;
 587     }
 588 
 589     @Override
 590     public int putMethodHandle(int refKind, S owner, CharSequence name, T type) {
 591         return putMethodHandle(refKind, owner, name, type, false);
 592     }
 593 
 594     @Override
 595     public int putMethodHandle(int refKind, S owner, CharSequence name, T type, boolean isInterface) {
 596         return putMethodHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type), isInterface);
 597     }
 598 
 599     private int putMethodHandleInternal(int refKind, String owner, CharSequence name, String type, boolean isInterface) {
 600         key.setMethodHandle(refKind, owner, name, type);
 601         PoolKey poolKey = entries.lookup(key);
 602         if (poolKey == null) {
 603             poolKey = key.dup();
 604             int ref_idx = putMemberRefInternal(fromKind(refKind, isInterface), owner, name, type);
 605             poolKey.at(builder.putMethodHandle(refKind, ref_idx));
 606             entries.enter(poolKey);
 607         }
 608         return poolKey.index;
 609     }
 610 
 611     @Override
 612     public int putValue(Object v) {
 613         if (v instanceof Class<?>) {
 614             return putClassInternal(((Class<?>) v).getName().replaceAll("\\.", "/"));
 615         } else if (v instanceof String) {
 616             key.setString((String) v);
 617             PoolKey poolKey = entries.lookup(key);
 618             if (poolKey == null) {
 619                 poolKey = key.dup();
 620                 int utf8_index = putUtf8((String) v);
 621                 poolKey.at(builder.putString(utf8_index));
 622                 entries.enter(poolKey);
 623             }
 624             return poolKey.index;
 625         } else if (v instanceof Integer) {
 626             key.setInteger((Integer) v);
 627             PoolKey poolKey = entries.lookup(key);
 628             if (poolKey == null) {
 629                 poolKey = key.dup();
 630                 poolKey.at(builder.putInt((Integer) v));
 631                 entries.enter(poolKey);
 632             }
 633             return poolKey.index;
 634         } else if (v instanceof Float) {
 635             key.setFloat((Float) v);
 636             PoolKey poolKey = entries.lookup(key);
 637             if (poolKey == null) {
 638                 poolKey = key.dup();
 639                 poolKey.at(builder.putFloat((Float) v));
 640                 entries.enter(poolKey);
 641             }
 642             return poolKey.index;
 643         } else if (v instanceof Double) {
 644             key.setDouble((Double) v);
 645             PoolKey poolKey = entries.lookup(key);
 646             if (poolKey == null) {
 647                 poolKey = key.dup();
 648                 poolKey.at(builder.putDouble((Double) v));
 649                 entries.enter(poolKey);
 650             }
 651             return poolKey.index;
 652         } else if (v instanceof Long) {
 653             key.setLong((Long) v);
 654             PoolKey poolKey = entries.lookup(key);
 655             if (poolKey == null) {
 656                 poolKey = key.dup();
 657                 poolKey.at(builder.putLong((Long) v));
 658                 entries.enter(poolKey);
 659             }
 660             return poolKey.index;
 661         } else if (v instanceof MethodHandle) {
 662             key.setMethodHandle((MethodHandle) v);
 663             PoolKey poolKey = entries.lookup(key);
 664             if (poolKey == null) {
 665                 poolKey = key.dup();
 666                 MethodHandle mh = (MethodHandle) v;
 667                 Member member = memberFromHandle(mh);
 668                 // ## TODO
 669                 String type = null;   // type from handle
 670                 int refKind = 0;      // kind for member
 671                 PoolTag tag = null;   // tag for member
 672                 int ref_idx = putMemberRefInternal(tag,
 673                     member.getDeclaringClass().getSimpleName(),
 674                     member.getName(),
 675                     type);
 676                 poolKey.at(builder.putMethodHandle(refKind, ref_idx));
 677                 entries.enter(poolKey);
 678             }
 679             return poolKey.index;
 680         } else if (v instanceof MethodType) {
 681             key.setMethodType((MethodType) v);
 682             PoolKey poolKey = entries.lookup(key);
 683             if (poolKey == null) {
 684                 poolKey = key.dup();
 685                 MethodType mt = (MethodType) v;
 686                 int desc_idx = putUtf8(mt.toMethodDescriptorString());
 687                 poolKey.at(builder.putMethodType(desc_idx));
 688                 entries.enter(poolKey);
 689             }
 690             return poolKey.index;
 691         } else {
 692             throw new UnsupportedOperationException("Unsupported object class: " + v.getClass().getName());
 693         }
 694     }
 695 
 696     @SuppressWarnings({"unchecked", "rawtypes"})
 697     private Member memberFromHandle(MethodHandle mh) {
 698         Class<Member>[] targets = new Class[]{Method.class, Field.class, Constructor.class};
 699         for (Class<Member> target : targets) {
 700             try {
 701                 return MethodHandles.reflectAs(target, mh);
 702             } catch (ClassCastException ex) {
 703                 //swallow
 704             }
 705         }
 706         throw new UnsupportedOperationException("Cannot crack method handle!");
 707     }
 708 
 709     PoolTag fromKind(int bsmKind, boolean isInterface) {
 710         switch (bsmKind) {
 711             case 1: // REF_getField
 712             case 2: // REF_getStatic
 713             case 3: // REF_putField
 714             case 4: // REF_putStatic
 715                 return PoolTag.FIELDREF;
 716             case 5: // REF_invokeVirtual
 717             case 7: // REF_invokeSpecial
 718             case 8: // REF_newInvokeSpecial
 719                 return PoolTag.METHODREF;
 720             case 6: // REF_invokeStatic
 721             case 9: // REF_invokeInterface
 722                 return isInterface ? PoolTag.INTERFACEMETHODREF : PoolTag.METHODREF;
 723             default:
 724                 throw new IllegalStateException();
 725         }
 726     }
 727 
 728     @Override
 729     public int putType(T s) {
 730         return putUtf8(typeToString.apply(s));
 731     }
 732 
 733     @Override
 734     public int putUtf8(CharSequence s) {
 735         key.setUtf8(s);
 736         PoolKey poolKey = entries.lookup(key);
 737         if (poolKey == null) {
 738             poolKey = key.dup();
 739             poolKey.at(builder.putUtf8(s));
 740             entries.enter(poolKey);
 741         }
 742         return poolKey.index;
 743     }
 744 
 745     @Override
 746     public int putString(String s) {
 747         key.setString(s);
 748         PoolKey poolKey = entries.lookup(key);
 749         if (poolKey == null) {
 750             poolKey = key.dup();
 751             int utf8_index = putUtf8(s);
 752             poolKey.at(builder.putString(utf8_index));
 753             entries.enter(poolKey);
 754         }
 755         return poolKey.index;
 756     }
 757 
 758     public int putNameAndType(CharSequence name, String type) {
 759         key.setNameAndType(name, type);
 760         PoolKey poolKey = entries.lookup(key);
 761         if (poolKey == null) {
 762             poolKey = key.dup();
 763             int name_idx = putUtf8(name);
 764             int type_idx = putUtf8(type);
 765             poolKey.at(builder.putNameAndType(name_idx, type_idx));
 766             entries.enter(poolKey);
 767         }
 768         return poolKey.index;
 769     }
 770 
 771     /**
 772      * @return the count of constant pool indicies.
 773      */
 774     @Override
 775     public int size() {
 776         return builder.size();
 777     }
 778 
 779     /**
 780      * @return the size in bytes of all constant pool entries.
 781      */
 782     @Override
 783     public byte[] representation() {
 784         return builder.representation();
 785     }
 786 
 787     <Z extends ClassBuilder<S, T, Z>> void addAttributes(ClassBuilder<S , T, Z> cb) {
 788         if (currentBsmIndex > 0) {
 789             GrowableByteBuffer bsmAttrBuf = new GrowableByteBuffer();
 790             bsmAttrBuf.writeChar(currentBsmIndex);
 791             bsmAttrBuf.writeBytes(bsm_attr);
 792             cb.withAttribute("BootstrapMethods", bsmAttrBuf.bytes());
 793         }
 794     }
 795 }