| /* |
| * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.experimental.bytecode; |
| |
| import java.lang.invoke.MethodHandleInfo; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.function.Consumer; |
| import java.util.function.Function; |
| import java.util.function.ToIntBiFunction; |
| |
| /** |
| * A helper for building and tracking constant pools whose entries are |
| * represented as byte arrays. |
| * |
| * @param <S> the type of the symbol representation |
| * @param <T> the type of type descriptors representation |
| */ |
| public class BytePoolHelper<S, T> implements PoolHelper<S, T, byte[]> { |
| |
| GrowableByteBuffer pool = new GrowableByteBuffer(); |
| GrowableByteBuffer bsm_attr = new GrowableByteBuffer(); |
| //Map<PoolKey, PoolKey> indicesMap = new HashMap<>(); |
| int currentIndex = 1; |
| int currentBsmIndex = 0; |
| |
| KeyMap<PoolKey> entries = new KeyMap<>(); |
| KeyMap<BsmKey> bootstraps = new KeyMap<>(); |
| PoolKey key = new PoolKey(); |
| BsmKey bsmKey = new BsmKey(); |
| |
| Function<S, String> symbolToString; |
| Function<T, String> typeToString; |
| |
| public BytePoolHelper(Function<S, String> symbolToString, Function<T, String> typeToString) { |
| this.symbolToString = symbolToString; |
| this.typeToString = typeToString; |
| } |
| |
| static class KeyMap<K extends AbstractKey<K>> { |
| |
| @SuppressWarnings("unchecked") |
| K[] table = (K[])new AbstractKey<?>[0x10]; |
| int nelems; |
| |
| public void enter(K e) { |
| if (nelems * 3 >= (table.length - 1) * 2) |
| dble(); |
| int hash = getIndex(e); |
| K old = table[hash]; |
| if (old == null) { |
| nelems++; |
| } |
| e.next = old; |
| table[hash] = e; |
| } |
| |
| protected K lookup(K other) { |
| K e = table[getIndex(other)]; |
| while (e != null && !e.equals(other)) |
| e = e.next; |
| return e; |
| } |
| |
| /** |
| * Look for slot in the table. |
| * We use open addressing with double hashing. |
| */ |
| int getIndex(K e) { |
| int hashMask = table.length - 1; |
| int h = e.hashCode(); |
| int i = h & hashMask; |
| // The expression below is always odd, so it is guaranteed |
| // to be mutually prime with table.length, a power of 2. |
| int x = hashMask - ((h + (h >> 16)) << 1); |
| for (; ; ) { |
| K e2 = table[i]; |
| if (e2 == null) |
| return i; |
| else if (e.hash == e2.hash) |
| return i; |
| i = (i + x) & hashMask; |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void dble() { |
| K[] oldtable = table; |
| table = (K[])new AbstractKey<?>[oldtable.length * 2]; |
| int n = 0; |
| for (int i = oldtable.length; --i >= 0; ) { |
| K e = oldtable[i]; |
| if (e != null) { |
| table[getIndex(e)] = e; |
| n++; |
| } |
| } |
| // We don't need to update nelems for shared inherited scopes, |
| // since that gets handled by leave(). |
| nelems = n; |
| } |
| } |
| |
| public static abstract class AbstractKey<K extends AbstractKey<K>> { |
| int hash; |
| int index = -1; |
| K next; |
| |
| abstract K dup(); |
| |
| public abstract boolean equals(Object o); |
| |
| @Override |
| public int hashCode() { |
| return hash; |
| } |
| |
| void at(int index) { |
| this.index = index; |
| } |
| } |
| |
| public static class PoolKey extends AbstractKey<PoolKey> { |
| PoolTag tag; |
| Object o1; |
| Object o2; |
| Object o3; |
| Object o4; |
| int size = -1; |
| |
| void setUtf8(CharSequence s) { |
| tag = PoolTag.CONSTANT_UTF8; |
| o1 = s; |
| size = 1; |
| hash = tag.tag | (s.hashCode() << 1); |
| } |
| |
| void setClass(String clazz) { |
| tag = PoolTag.CONSTANT_CLASS; |
| o1 = clazz; |
| size = 1; |
| hash = tag.tag | (clazz.hashCode() << 1); |
| } |
| |
| void setNameAndType(CharSequence name, String type) { |
| tag = PoolTag.CONSTANT_NAMEANDTYPE; |
| o1 = name; |
| o2 = type; |
| size = 2; |
| hash = tag.tag | ((name.hashCode() | type.hashCode()) << 1); |
| } |
| |
| void setMemberRef(PoolTag poolTag, String owner, CharSequence name, String type) { |
| tag = poolTag; |
| o1 = owner; |
| o2 = name; |
| o3 = type; |
| size = 3; |
| hash = tag.tag | ((owner.hashCode() | name.hashCode() | type.hashCode()) << 1); |
| } |
| |
| void setInvokeDynamic(int bsmIndex, CharSequence name, String type) { |
| tag = PoolTag.CONSTANT_INVOKEDYNAMIC; |
| o1 = bsmIndex; |
| o2 = name; |
| o3 = type; |
| size = 3; |
| hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1); |
| } |
| |
| void setDynamicConstant(int bsmIndex, CharSequence name, String type) { |
| tag = PoolTag.CONSTANT_DYNAMIC; |
| o1 = bsmIndex; |
| o2 = name; |
| o3 = type; |
| size = 3; |
| hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1); |
| } |
| |
| void setString(String s) { |
| tag = PoolTag.CONSTANT_STRING; |
| o1 = s; |
| size = 1; |
| hash = tag.tag | (s.hashCode() << 1); |
| } |
| |
| void setInteger(Integer i) { |
| tag = PoolTag.CONSTANT_INTEGER; |
| o1 = i; |
| size = 1; |
| hash = tag.tag | (i.hashCode() << 1); |
| } |
| |
| void setFloat(Float f) { |
| tag = PoolTag.CONSTANT_FLOAT; |
| o1 = f; |
| size = 1; |
| hash = tag.tag | (f.hashCode() << 1); |
| } |
| |
| void setLong(Long l) { |
| tag = PoolTag.CONSTANT_LONG; |
| o1 = l; |
| size = 1; |
| hash = tag.tag | (l.hashCode() << 1); |
| } |
| |
| void setDouble(Double d) { |
| tag = PoolTag.CONSTANT_DOUBLE; |
| o1 = d; |
| size = 1; |
| hash = tag.tag | (d.hashCode() << 1); |
| } |
| |
| void setMethodType(String type) { |
| tag = PoolTag.CONSTANT_METHODTYPE; |
| o1 = type; |
| size = 1; |
| hash = tag.tag | (type.hashCode() << 1); |
| } |
| |
| void setMethodHandle(int bsmKind, String owner, CharSequence name, String type) { |
| tag = PoolTag.CONSTANT_METHODHANDLE; |
| o1 = bsmKind; |
| o2 = owner; |
| o3 = name; |
| o4 = type; |
| size = 4; |
| hash = tag.tag | (bsmKind | owner.hashCode() | name.hashCode() | type.hashCode() << 1); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| PoolKey that = (PoolKey) obj; |
| if (tag != that.tag) return false; |
| switch (size) { |
| case 1: |
| if (!o1.equals(that.o1)) { |
| return false; |
| } |
| break; |
| case 2: |
| if (!o2.equals(that.o2) || !o1.equals(that.o1)) { |
| return false; |
| } |
| break; |
| case 3: |
| if (!o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) { |
| return false; |
| } |
| break; |
| case 4: |
| if (!o4.equals(that.o4) || !o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) { |
| return false; |
| } |
| break; |
| } |
| return true; |
| } |
| |
| PoolKey dup() { |
| PoolKey poolKey = new PoolKey(); |
| poolKey.tag = tag; |
| poolKey.size = size; |
| poolKey.hash = hash; |
| poolKey.o1 = o1; |
| poolKey.o2 = o2; |
| poolKey.o3 = o3; |
| poolKey.o4 = o4; |
| return poolKey; |
| } |
| } |
| |
| static class BsmKey extends AbstractKey<BsmKey> { |
| String bsmClass; |
| CharSequence bsmName; |
| String bsmType; |
| List<Integer> bsmArgs; |
| |
| void set(String bsmClass, CharSequence bsmName, String bsmType, List<Integer> bsmArgs) { |
| this.bsmClass = bsmClass; |
| this.bsmName = bsmName; |
| this.bsmType = bsmType; |
| this.bsmArgs = bsmArgs; |
| hash = bsmClass.hashCode() | bsmName.hashCode() | bsmType.hashCode() | Objects.hash(bsmArgs); |
| } |
| |
| BsmKey dup() { |
| BsmKey bsmKey = new BsmKey(); |
| bsmKey.bsmClass = bsmClass; |
| bsmKey.bsmName = bsmName; |
| bsmKey.bsmType = bsmType; |
| bsmKey.bsmArgs = bsmArgs; |
| bsmKey.hash = hash; |
| return bsmKey; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof BsmKey) { |
| BsmKey that = (BsmKey)obj; |
| return Objects.equals(bsmClass, that.bsmClass) && |
| Objects.equals(bsmName, that.bsmName) && |
| Objects.equals(bsmType, that.bsmType) && |
| Objects.deepEquals(bsmArgs, that.bsmArgs); |
| } else { |
| return false; |
| } |
| } |
| } |
| |
| @Override |
| public int putClass(S symbol) { |
| return putClassInternal(symbolToString.apply(symbol)); |
| } |
| |
| private int putClassInternal(String symbol) { |
| key.setClass(symbol); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| int utf8_idx = putUtf8(symbol); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_CLASS.tag); |
| pool.writeChar(utf8_idx); |
| } |
| return poolKey.index; |
| } |
| |
| @Override |
| public int putFieldRef(S owner, CharSequence name, T type) { |
| return putMemberRef(PoolTag.CONSTANT_FIELDREF, owner, name, type); |
| } |
| |
| @Override |
| public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) { |
| return putMemberRef(isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF, |
| owner, name, type); |
| } |
| |
| int putMemberRef(PoolTag poolTag, S owner, CharSequence name, T type) { |
| return putMemberRefInternal(poolTag, symbolToString.apply(owner), name, typeToString.apply(type)); |
| } |
| |
| int putMemberRefInternal(PoolTag poolTag, String owner, CharSequence name, String type) { |
| key.setMemberRef(poolTag, owner, name, type); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| int owner_idx = putClassInternal(owner); |
| int nameAndType_idx = putNameAndType(name, type); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(poolTag.tag); |
| pool.writeChar(owner_idx); |
| pool.writeChar(nameAndType_idx); |
| } |
| return poolKey.index; |
| } |
| |
| @Override |
| public int putInt(int i) { |
| key.setInteger(i); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_INTEGER.tag); |
| pool.writeInt(i); |
| } |
| return poolKey.index; |
| } |
| |
| @Override |
| public int putFloat(float f) { |
| key.setFloat(f); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_FLOAT.tag); |
| pool.writeFloat(f); |
| } |
| return poolKey.index; |
| } |
| |
| @Override |
| public int putLong(long l) { |
| key.setLong(l); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_LONG.tag); |
| pool.writeLong(l); |
| currentIndex++; |
| } |
| return poolKey.index; |
| } |
| |
| @Override |
| public int putDouble(double d) { |
| key.setDouble(d); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_DOUBLE.tag); |
| pool.writeDouble(d); |
| currentIndex++; |
| } |
| return poolKey.index; |
| } |
| |
| |
| @Override |
| public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) { |
| return putInvokeDynamicInternal(invokedName, typeToString.apply(invokedType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs); |
| } |
| |
| @Override |
| public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) { |
| return putDynamicConstantInternal(constName, typeToString.apply(constType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs); |
| } |
| |
| private int putInvokeDynamicInternal(CharSequence invokedName, String invokedType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) { |
| int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs); |
| key.setInvokeDynamic(bsmIndex, invokedName, invokedType); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| int nameAndType_idx = putNameAndType(invokedName, invokedType); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_INVOKEDYNAMIC.tag); |
| pool.writeChar(bsmIndex); |
| pool.writeChar(nameAndType_idx); |
| } |
| return poolKey.index; |
| } |
| |
| private int putDynamicConstantInternal(CharSequence constName, String constType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) { |
| int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs); |
| key.setDynamicConstant(bsmIndex, constName, constType); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| int nameAndType_idx = putNameAndType(constName, constType); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_DYNAMIC.tag); |
| pool.writeChar(bsmIndex); |
| pool.writeChar(nameAndType_idx); |
| } |
| return poolKey.index; |
| } |
| |
| private int putBsmInternal(String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) { |
| ByteStaticArgListBuilder staticArgsBuilder = new ByteStaticArgListBuilder(); |
| staticArgs.accept(staticArgsBuilder); |
| List<Integer> static_idxs = staticArgsBuilder.indexes; |
| bsmKey.set(bsmClass, bsmName, bsmType, static_idxs); |
| BsmKey poolKey = bootstraps.lookup(bsmKey); |
| if (poolKey == null) { |
| poolKey = bsmKey.dup(); |
| // TODO the BSM could be a static method on an interface |
| int bsm_ref = putHandleInternal(MethodHandleInfo.REF_invokeStatic, bsmClass, bsmName, bsmType, false); |
| poolKey.at(currentBsmIndex++); |
| bootstraps.enter(poolKey); |
| bsm_attr.writeChar(bsm_ref); |
| bsm_attr.writeChar(static_idxs.size()); |
| for (int i : static_idxs) { |
| bsm_attr.writeChar(i); |
| } |
| } |
| return poolKey.index; |
| } |
| //where |
| class ByteStaticArgListBuilder implements StaticArgListBuilder<S, T, byte[]> { |
| |
| List<Integer> indexes = new ArrayList<>(); |
| |
| public ByteStaticArgListBuilder add(int i) { |
| indexes.add(putInt(i)); |
| return this; |
| } |
| public ByteStaticArgListBuilder add(float f) { |
| indexes.add(putFloat(f)); |
| return this; |
| } |
| public ByteStaticArgListBuilder add(long l) { |
| indexes.add(putLong(l)); |
| return this; |
| } |
| public ByteStaticArgListBuilder add(double d) { |
| indexes.add(putDouble(d)); |
| return this; |
| } |
| public ByteStaticArgListBuilder add(String s) { |
| indexes.add(putString(s)); |
| return this; |
| } |
| @Override |
| public StaticArgListBuilder<S, T, byte[]> add(int refKind, S owner, CharSequence name, T type) { |
| indexes.add(putHandle(refKind, owner, name, type)); |
| return this; |
| } |
| public <Z> ByteStaticArgListBuilder add(Z z, ToIntBiFunction<PoolHelper<S, T, byte[]>, Z> poolFunc) { |
| indexes.add(poolFunc.applyAsInt(BytePoolHelper.this, z)); |
| return this; |
| } |
| public ByteStaticArgListBuilder add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) { |
| indexes.add(putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs)); |
| return this; |
| } |
| } |
| |
| @Override |
| public int putMethodType(T s) { |
| return putMethodTypeInternal(typeToString.apply(s)); |
| } |
| |
| private int putMethodTypeInternal(String s) { |
| key.setMethodType(s); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| int desc_idx = putUtf8(s); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_METHODTYPE.tag); |
| pool.writeChar(desc_idx); |
| } |
| return poolKey.index; |
| } |
| |
| @Override |
| public int putHandle(int refKind, S owner, CharSequence name, T type) { |
| return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type), false); |
| } |
| |
| @Override |
| public int putHandle(int refKind, S owner, CharSequence name, T type, boolean isInterface) { |
| return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type), isInterface); |
| } |
| |
| private int putHandleInternal(int refKind, String owner, CharSequence name, String type, boolean isInterface) { |
| key.setMethodHandle(refKind, owner, name, type); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| int ref_idx = putMemberRefInternal(fromKind(refKind, isInterface), owner, name, type); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_METHODHANDLE.tag); |
| pool.writeByte(refKind); |
| pool.writeChar(ref_idx); |
| } |
| return poolKey.index; |
| } |
| |
| PoolTag fromKind(int bsmKind, boolean isInterface) { |
| switch (bsmKind) { |
| case 1: // REF_getField |
| case 2: // REF_getStatic |
| case 3: // REF_putField |
| case 4: // REF_putStatic |
| return PoolTag.CONSTANT_FIELDREF; |
| case 5: // REF_invokeVirtual |
| case 6: // REF_invokeStatic |
| case 7: // REF_invokeSpecial |
| case 8: // REF_newInvokeSpecial |
| case 9: // REF_invokeInterface |
| return isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF; |
| default: |
| throw new IllegalStateException(); |
| } |
| } |
| |
| @Override |
| public int putType(T s) { |
| return putUtf8(typeToString.apply(s)); |
| } |
| |
| public int putUtf8(CharSequence s) { |
| key.setUtf8(s); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_UTF8.tag); |
| putUTF8Internal(s); |
| } |
| return poolKey.index; |
| } |
| |
| /** |
| * Puts an UTF8 string into this byte vector. The byte vector is |
| * automatically enlarged if necessary. |
| * |
| * @param s a String whose UTF8 encoded length must be less than 65536. |
| * @return this byte vector. |
| */ |
| void putUTF8Internal(final CharSequence s) { |
| int charLength = s.length(); |
| if (charLength > 65535) { |
| throw new IllegalArgumentException(); |
| } |
| // optimistic algorithm: instead of computing the byte length and then |
| // serializing the string (which requires two loops), we assume the byte |
| // length is equal to char length (which is the most frequent case), and |
| // we start serializing the string right away. During the serialization, |
| // if we find that this assumption is wrong, we continue with the |
| // general method. |
| pool.writeChar(charLength); |
| for (int i = 0; i < charLength; ++i) { |
| char c = s.charAt(i); |
| if (c >= '\001' && c <= '\177') { |
| pool.writeByte((byte) c); |
| } else { |
| encodeUTF8(s, i, 65535); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Puts an UTF8 string into this byte vector. The byte vector is |
| * automatically enlarged if necessary. The string length is encoded in two |
| * bytes before the encoded characters, if there is space for that (i.e. if |
| * this.length - i - 2 >= 0). |
| * |
| * @param s the String to encode. |
| * @param i the index of the first character to encode. The previous |
| * characters are supposed to have already been encoded, using |
| * only one byte per character. |
| * @param maxByteLength the maximum byte length of the encoded string, including the |
| * already encoded characters. |
| * @return this byte vector. |
| */ |
| void encodeUTF8(final CharSequence s, int i, int maxByteLength) { |
| int charLength = s.length(); |
| int byteLength = i; |
| char c; |
| for (int j = i; j < charLength; ++j) { |
| c = s.charAt(j); |
| if (c >= '\001' && c <= '\177') { |
| byteLength++; |
| } else if (c > '\u07FF') { |
| byteLength += 3; |
| } else { |
| byteLength += 2; |
| } |
| } |
| if (byteLength > maxByteLength) { |
| throw new IllegalArgumentException(); |
| } |
| int byteLengthFinal = byteLength; |
| pool.withOffset(pool.offset - i - 2, buf -> buf.writeChar(byteLengthFinal)); |
| for (int j = i; j < charLength; ++j) { |
| c = s.charAt(j); |
| if (c >= '\001' && c <= '\177') { |
| pool.writeChar((byte) c); |
| } else if (c > '\u07FF') { |
| pool.writeChar((byte) (0xE0 | c >> 12 & 0xF)); |
| pool.writeChar((byte) (0x80 | c >> 6 & 0x3F)); |
| pool.writeChar((byte) (0x80 | c & 0x3F)); |
| } else { |
| pool.writeChar((byte) (0xC0 | c >> 6 & 0x1F)); |
| pool.writeChar((byte) (0x80 | c & 0x3F)); |
| } |
| } |
| } |
| |
| @Override |
| public int putString(String s) { |
| key.setString(s); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| int utf8_index = putUtf8(s); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_STRING.tag); |
| pool.writeChar(utf8_index); |
| } |
| return poolKey.index; |
| } |
| |
| int putNameAndType(CharSequence name, String type) { |
| key.setNameAndType(name, type); |
| PoolKey poolKey = entries.lookup(key); |
| if (poolKey == null) { |
| poolKey = key.dup(); |
| int name_idx = putUtf8(name); |
| int type_idx = putUtf8(type); |
| poolKey.at(currentIndex++); |
| entries.enter(poolKey); |
| pool.writeByte(PoolTag.CONSTANT_NAMEANDTYPE.tag); |
| pool.writeChar(name_idx); |
| pool.writeChar(type_idx); |
| } |
| return poolKey.index; |
| } |
| |
| @Override |
| public int size() { |
| return currentIndex - 1; |
| } |
| |
| @Override |
| public byte[] entries() { |
| return pool.bytes(); |
| } |
| |
| <Z extends ClassBuilder<S, T, Z>> void addAttributes(ClassBuilder<S , T, Z> cb) { |
| if (currentBsmIndex > 0) { |
| GrowableByteBuffer bsmAttrBuf = new GrowableByteBuffer(); |
| bsmAttrBuf.writeChar(currentBsmIndex); |
| bsmAttrBuf.writeBytes(bsm_attr); |
| cb.withAttribute("BootstrapMethods", bsmAttrBuf.bytes()); |
| } |
| } |
| } |