Commit 648c3471 authored by Remi Forax's avatar Remi Forax

add support of CONSTANT_Dynamic_info

parent 3cee35fc
Pipeline #568 passed with stage
in 6 minutes and 34 seconds
......@@ -29,6 +29,7 @@ package org.objectweb.asm.tree.analysis;
import java.util.List;
import org.objectweb.asm.Condy;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
......@@ -154,6 +155,8 @@ public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes
}
} else if (value instanceof Handle) {
return newValue(Type.getObjectType("java/lang/invoke/MethodHandle"));
} else if (value instanceof Condy) {
return newValue(Type.getType(((Condy)value).getDescriptor()));
} else {
throw new AnalyzerException(insn, "Illegal LDC value " + value);
}
......
......@@ -32,6 +32,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Condy;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
......@@ -411,6 +412,8 @@ public class AnalyzerAdapter extends MethodVisitor {
}
} else if (cst instanceof Handle) {
push("java/lang/invoke/MethodHandle");
} else if (cst instanceof Condy) {
pushDesc(((Condy)cst).getDescriptor());
} else {
throw new IllegalArgumentException();
}
......
......@@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Condy;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
......@@ -1359,6 +1360,21 @@ public class ASMifier extends Printer {
stringBuilder.append(handle.getName()).append("\", \"");
stringBuilder.append(handle.getDesc()).append("\", ");
stringBuilder.append(handle.isInterface()).append(")");
} else if (value instanceof Condy) {
stringBuilder.append("new Condy(\"");
Condy c = (Condy) value;
stringBuilder.append(c.getName()).append("\", \"");
stringBuilder.append(c.getDescriptor()).append("\", ");
appendConstant(c.getBootrapMethod());
stringBuilder.append(", new Object[] {");
Object[] bootstrapArguments = c.getBootstrapArguments();
for (int i = 0; i < bootstrapArguments.length; ++i) {
appendConstant(bootstrapArguments[i]);
if (i != bootstrapArguments.length - 1) {
stringBuilder.append(", ");
}
}
stringBuilder.append("})");
} else if (value instanceof Byte) {
stringBuilder.append("new Byte((byte)").append(value).append(')');
} else if (value instanceof Boolean) {
......
......@@ -38,6 +38,7 @@ import java.util.Set;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Condy;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
......@@ -775,8 +776,8 @@ public class CheckMethodAdapter extends MethodVisitor {
&& bootstrapMethodHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
throw new IllegalArgumentException("invalid handle tag " + bootstrapMethodHandle.getTag());
}
for (int i = 0; i < bootstrapMethodArguments.length; i++) {
checkLdcConstant(bootstrapMethodArguments[i]);
for (Object bootstrapMethodArgument: bootstrapMethodArguments) {
checkLdcConstant(bootstrapMethodArgument);
}
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
++insnCount;
......@@ -1186,10 +1187,29 @@ public class CheckMethodAdapter extends MethodVisitor {
if ((version & 0xFFFF) < Opcodes.V1_7) {
throw new IllegalArgumentException("ldc of a handle requires at least version 1.7");
}
int tag = ((Handle) value).getTag();
Handle handle = (Handle) value;
int tag = handle.getTag();
if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) {
throw new IllegalArgumentException("invalid handle tag " + tag);
}
checkInternalName(handle.getOwner(), "handle owner");
if (tag <= Opcodes.H_PUTSTATIC) {
checkDescriptor(handle.getDesc(), false);
} else {
checkMethodDescriptor(handle.getDesc());
}
checkMethodIdentifier(this.version, handle.getName(), "handle name");
} else if (value instanceof Condy) {
if ((version & 0xFFFF) < Opcodes.V11) {
throw new IllegalArgumentException("ldc of a handle requires at least version 1.7");
}
Condy condy = (Condy) value;
checkMethodIdentifier(this.version, condy.getName(), "constant dynamic name");
checkDescriptor(condy.getDescriptor(), false);
checkLdcConstant(condy.getBootrapMethod());
for (Object bootstrapArgument: condy.getBootstrapArguments()) {
checkLdcConstant(bootstrapArgument);
}
} else {
checkConstant(value);
}
......
......@@ -35,9 +35,11 @@ import java.util.List;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Condy;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;
/**
......@@ -926,9 +928,10 @@ public abstract class Printer {
*
* @param value the constant to be loaded on the stack. This parameter must be a non null {@link
* Integer}, a {@link Float}, a {@link Long}, a {@link Double}, a {@link String}, a {@link
* org.objectweb.asm.Type} of OBJECT or ARRAY sort for <tt>.class</tt> constants, for classes
* whose version is 49.0, a {@link org.objectweb.asm.Type} of METHOD sort or a {@link Handle}
* for MethodType and MethodHandle constants, for classes whose version is 51.0.
* Type} of OBJECT or ARRAY sort for <tt>.class</tt> constants, for classes whose version is
* 49, a {@link Type} of METHOD sort for MethodType, a {@link Handle} for MethodHandle
* constants, for classes whose version is 51 or a {@link Condy} for a constant dynamic for
* classes whose version is 55.
*/
public abstract void visitLdcInsn(Object value);
......
......@@ -106,10 +106,19 @@ public class ClassReader {
private final int[] cpInfoOffsets;
/**
* The String objects corresponding to the CONSTANT_Utf8 items. This cache avoids multiple parsing
* of a given CONSTANT_Utf8 constant pool item.
* The String or Condy objects respectively corresponding to the CONSTANT_Utf8 and
* CONSTANT_Dynamic items. This cache avoids multiple parsing of those constant pool items.
*/
private final String[] constantUtf8Values;
private final Object[] cacheValues;
/**
* The start offsets in {@link ClassReader#b} of each element of the bootstrap_methods array (in
* the BootstrapMethod attribute).
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
* 4.7.23</a>
*/
int[] bootstrapMethodOffsets;
/**
* A conservative estimate of the maximum length of the strings contained in the constant pool of
......@@ -152,7 +161,7 @@ public class ClassReader {
// minor_version and major_version fields, which use 4, 2 and 2 bytes respectively.
int constantPoolCount = readUnsignedShort(classFileOffset + 8);
cpInfoOffsets = new int[constantPoolCount];
constantUtf8Values = new String[constantPoolCount];
cacheValues = new Object[constantPoolCount];
// Compute the offset of each constant pool entry, as well as a conservative estimate of the
// maximum length of the constant pool strings. The first constant pool entry is after the
// magic, minor_version, major_version and constant_pool_count fields, which use 4, 2, 2 and 2
......@@ -172,6 +181,7 @@ public class ClassReader {
case Symbol.CONSTANT_FLOAT_TAG:
case Symbol.CONSTANT_NAME_AND_TYPE_TAG:
case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
case Symbol.CONSTANT_DYNAMIC_TAG:
cpInfoSize = 5;
break;
case Symbol.CONSTANT_LONG_TAG:
......@@ -453,7 +463,7 @@ public class ClassReader {
currentBootstrapMethodOffset +=
4 + readUnsignedShort(currentBootstrapMethodOffset + 2) * 2;
}
context.bootstrapMethodOffsets = bootstrapMethodOffsets;
this.bootstrapMethodOffsets = bootstrapMethodOffsets;
} else {
Attribute attribute =
readAttribute(
......@@ -2170,8 +2180,7 @@ public class ClassReader {
int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
int bootstrapMethodOffset =
context.bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
Handle handle =
(Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
Object[] boostrapMethodArguments =
......@@ -3331,13 +3340,14 @@ public class ClassReader {
* @return the String corresponding to the specified CONSTANT_Utf8 entry.
*/
final String readUTF(final int constantPoolEntryIndex, final char[] charBuffer) {
String value = constantUtf8Values[constantPoolEntryIndex];
String value = (String) cacheValues[constantPoolEntryIndex];
if (value != null) {
return value;
}
int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
return constantUtf8Values[constantPoolEntryIndex] =
readUTF(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer);
value = readUTF(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer);
cacheValues[constantPoolEntryIndex] = value;
return value;
}
/**
......@@ -3432,17 +3442,63 @@ public class ClassReader {
return readStringish(offset, charBuffer);
}
/**
* Reads a CONSTANT_Dynamic constant pool entry in {@link #b}.
*
* @param constantPoolEntryIndex the index of a CONSTANT_Dynamic entry in the class's constant
* pool table.
* @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
* large. It is not automatically resized.
* @return the Condy corresponding to the specified CONSTANT_Dynamic entry.
*/
private Condy readConstantDynamic(final int constantPoolEntryIndex, final char[] charBuffer) {
Condy condy = (Condy) cacheValues[constantPoolEntryIndex];
if (condy != null) {
return condy;
}
int offset = cpInfoOffsets[constantPoolEntryIndex];
condy = decodeConstantDynamic(offset, charBuffer);
cacheValues[constantPoolEntryIndex] = condy;
return condy;
}
/**
* Reads a CONSTANT_Dynamic constant pool entry at a constant pool offset.
*
* @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
* index of a CONSTANT_Dynamic entry in class's constant pool table.
* @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
* large. It is not automatically resized.
* @return the Condy corresponding to the specified CONSTANT_Dynamic entry.
*/
private Condy decodeConstantDynamic(final int offset, final char[] charBuffer) {
int bsmIndex = bootstrapMethodOffsets[readUnsignedShort(offset)];
Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), charBuffer);
int bsmArgCount = readUnsignedShort(bsmIndex + 2);
Object[] bsmArgs = new Object[bsmArgCount];
bsmIndex += 4;
for (int i = 0; i < bsmArgCount; i++) {
bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), charBuffer);
bsmIndex += 2;
}
int cpOffset = cpInfoOffsets[readShort(offset + 2)];
String name = readUTF8(cpOffset, charBuffer);
String desc = readUTF8(cpOffset + 2, charBuffer);
return new Condy(name, desc, bsm, bsmArgs);
}
/**
* Reads a numeric or string constant pool entry in {@link #b}. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
*
* @param constantPoolEntryIndex the index of a CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long,
* CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType or
* CONSTANT_MethodHandle entry in the class's constant pool.
* CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
* CONSTANT_MethodHandle or CONSTANT_Dynamic entry in the class's constant pool.
* @param charBuffer the buffer to be used to read strings. This buffer must be sufficiently
* large. It is not automatically resized.
* @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String},
* {@link Type} or {@link Handle} corresponding to the specified constant pool entry.
* {@link Type}, {@link Handle} or {@link Condy} corresponding to the specified constant pool
* entry.
*/
public Object readConst(final int constantPoolEntryIndex, final char[] charBuffer) {
int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
......@@ -3471,6 +3527,8 @@ public class ClassReader {
boolean isInterface =
b[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
return new Handle(referenceKind, owner, name, descriptor, isInterface);
case Symbol.CONSTANT_DYNAMIC_TAG:
return readConstantDynamic(constantPoolEntryIndex, charBuffer);
default:
throw new IllegalArgumentException();
}
......
......@@ -743,6 +743,27 @@ public class ClassWriter extends ClassVisitor {
return symbolTable.addConstantMethodHandle(tag, owner, name, descriptor, isInterface).index;
}
/**
* Adds a dynamic constant reference to the constant pool of the class being build. Does nothing
* if the constant pool already contains a similar item. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
*
* @param name name of the invoked method.
* @param descriptor field descriptor of the constant type.
* @param bootstrapMethodHandle the bootstrap method.
* @param bootstrapMethodArguments the bootstrap method constant arguments.
* @return the index of a new or already existing dynamic constant reference item.
*/
public int newCondy(
final String name,
final String descriptor,
final Handle bootstrapMethodHandle,
final Object... bootstrapMethodArguments) {
return symbolTable.addConstantDynamic(
name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
.index;
}
/**
* Adds an invokedynamic reference to the constant pool of the class being build. Does nothing if
* the constant pool already contains a similar item. <i>This method is intended for {@link
......
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm;
import java.util.Arrays;
/**
* A class that represent a constant dynamic.
*
* @author Remi Forax
*/
public final class Condy {
  • We need to find another name for this class, without abbreviations. Before changing anything, we must agree on the new name. Some ideas:

    • ConstantDynamic, ConstantHandle, ConstantExpression (although the class itself is not specific to constants: it could be used as well for visitInvokeDynamic)
    • InvokeDynamic, BootstrapCall, BootstrapInvoke (to convey the 'invoke' and/or bootstrap aspect)
    • ...?
Please register or sign in to reply
final String name;
final String descriptor;
final Handle bootstrapMethod;
final Object[] bootstrapArguments;
/**
* Create a constant dynamic.
*
* @param name an arbitrary name
* @param descriptor a field descriptor
* @param bootstrapMethod a bootstrap method
* @param bootstrapArguments the arguments of the bootstrap method
*/
public Condy(
String name, String descriptor, Handle bootstrapMethod, Object... bootstrapArguments) {
this.name = name;
this.descriptor = descriptor;
this.bootstrapMethod = bootstrapMethod;
this.bootstrapArguments = bootstrapArguments;
}
/**
* Returns the name of the current constant dynamic.
*
* @return the name of the current constant dynamic.
*/
public String getName() {
return name;
}
/**
* Returns the field descriptor of the current constant dynamic.
*
* @return the field descriptor of the current constant dynamic.
*/
public String getDescriptor() {
return descriptor;
}
/**
* Returns the boostrap method of the current constant dynamic.
*
* @return the boostrap method of the current constant dynamic.
*/
public Handle getBootrapMethod() {
return bootstrapMethod;
}
/**
* Returns the bootstrap method arguments of the current constant dynamic.
*
* @return the bootstrap method arguments of the current constant dynamic.
*/
public Object[] getBootstrapArguments() {
return bootstrapArguments;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Condy)) {
return false;
}
Condy condy = (Condy) obj;
return name.equals(condy.name)
&& descriptor.equals(condy.descriptor)
&& bootstrapMethod.equals(condy.bootstrapMethod)
&& Arrays.equals(bootstrapArguments, condy.bootstrapArguments);
}
@Override
public int hashCode() {
return name.hashCode()
^ Integer.rotateLeft(descriptor.hashCode(), 8)
^ Integer.rotateLeft(bootstrapMethod.hashCode(), 16)
^ Integer.rotateLeft(Arrays.hashCode(bootstrapArguments), 24);
}
@Override
public String toString() {
return name
+ " : "
+ descriptor
+ ' '
+ bootstrapMethod
+ ' '
+ Arrays.toString(bootstrapArguments);
}
}
......@@ -48,15 +48,6 @@ final class Context {
/** The buffer used to read strings in the constant pool. */
char[] charBuffer;
/**
* The start offsets in {@link ClassReader#b} of each element of the bootstrap_methods array (in
* the BootstrapMethod attribute).
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
* 4.7.23</a>
*/
int[] bootstrapMethodOffsets;
// Information about the current method, i.e. the one read in the current (or latest) call
// to {@link ClassReader#readMethod()}.
......
......@@ -748,6 +748,9 @@ class Frame {
case Symbol.CONSTANT_METHOD_HANDLE_TAG:
push(REFERENCE_KIND | symbolTable.addType("java/lang/invoke/MethodHandle"));
break;
case Symbol.CONSTANT_DYNAMIC_TAG:
push(symbolTable, argSymbol.value);
break;
default:
throw new AssertionError();
}
......
......@@ -442,8 +442,8 @@ public abstract class MethodVisitor {
* @param bootstrapMethodHandle the bootstrap method.
* @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be
* an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link
* Type} or {@link Handle} value. This method is allowed to modify the content of the array so
* a caller should expect that this array may change.
* Type}, {@link Handle} or {@link Condy} value. This method is allowed to modify the content
* of the array so a caller should expect that this array may change.
*/
public void visitInvokeDynamicInsn(
final String name,
......@@ -518,6 +518,8 @@ public abstract class MethodVisitor {
* }
* } else if (cst instanceof Handle) {
* // ...
* } else if (cst instanceof Condy) {
* // ...
* } else {
* // throw an exception
* }
......@@ -526,8 +528,9 @@ public abstract class MethodVisitor {
* @param value the constant to be loaded on the stack. This parameter must be a non null {@link
* Integer}, a {@link Float}, a {@link Long}, a {@link Double}, a {@link String}, a {@link
* Type} of OBJECT or ARRAY sort for <tt>.class</tt> constants, for classes whose version is
* 49.0, a {@link Type} of METHOD sort or a {@link Handle} for MethodType and MethodHandle
* constants, for classes whose version is 51.0.
* 49, a {@link Type} of METHOD sort for MethodType, a {@link Handle} for MethodHandle
* constants, for classes whose version is 51 or a {@link Condy} for a constant dynamic for
* classes whose version is 55.
*/
public void visitLdcInsn(final Object value) {
if (mv != null) {
......
......@@ -59,6 +59,7 @@ public interface Opcodes {
int V1_8 = 0 << 16 | 52;
int V9 = 0 << 16 | 53;
int V10 = 0 << 16 | 54;
int V11 = 0 << 16 | 55;
// Access flags values, defined in
// - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1
......
......@@ -80,6 +80,9 @@ abstract class Symbol {
/** The tag value of CONSTANT_MethodType_info JVMS structures. */
static final int CONSTANT_METHOD_TYPE_TAG = 16;
/** The tag value of CONSTANT_Dynamic_info JVMS structures. */
static final int CONSTANT_DYNAMIC_TAG = 17;
/** The tag value of CONSTANT_InvokeDynamic_info JVMS structures. */
static final int CONSTANT_INVOKE_DYNAMIC_TAG = 18;
......@@ -132,7 +135,8 @@ abstract class Symbol {
* The name of the class field or method corresponding to this symbol. Only used for {@link
* #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link
* #CONSTANT_INTERFACE_METHODREF_TAG}, {@link #CONSTANT_NAME_AND_TYPE_TAG}, {@link
* #CONSTANT_METHOD_HANDLE_TAG} and {@link #CONSTANT_INVOKE_DYNAMIC_TAG} symbols.
* #CONSTANT_METHOD_HANDLE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link
* #CONSTANT_INVOKE_DYNAMIC_TAG} symbols.
*/
final String name;
......@@ -143,7 +147,8 @@ abstract class Symbol {
* <li>a field or method descriptor for {@link #CONSTANT_FIELDREF_TAG}, {@link
* #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG}, {@link
* #CONSTANT_NAME_AND_TYPE_TAG}, {@link #CONSTANT_METHOD_HANDLE_TAG}, {@link
* #CONSTANT_METHOD_TYPE_TAG} and {@link #CONSTANT_INVOKE_DYNAMIC_TAG} symbols,
* #CONSTANT_METHOD_TYPE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link
* #CONSTANT_INVOKE_DYNAMIC_TAG} symbols,
* <li>an arbitrary string for {@link #CONSTANT_UTF8_TAG} and {@link #CONSTANT_STRING_TAG}
* symbols,
* <li>an internal class name for {@link #CONSTANT_CLASS_TAG}, {@link #TYPE_TAG} and {@link
......@@ -164,7 +169,7 @@ abstract class Symbol {
* <li>the CONSTANT_InvokeDynamic_info bootstrap_method_attr_index field value for {@link
* #CONSTANT_INVOKE_DYNAMIC_TAG} symbols,
* <li>the offset of a bootstrap method in the BootstrapMethods boostrap_methods array, for
* {@link #BOOTSTRAP_METHOD_TAG} symbols,
* {@link #CONSTANT_DYNAMIC_TAG} or {@link #BOOTSTRAP_METHOD_TAG} symbols,
* <li>the bytecode offset of the NEW instruction that created an {@link
* Frame#ITEM_UNINITIALIZED} type for {@link #UNINITIALIZED_TYPE_TAG} symbols,
* <li>the indices (in the class' type table) of two {@link #TYPE_TAG} source types for {@link
......
......@@ -250,10 +250,12 @@ final class SymbolTable {
classReader.readUTF8(nameAndTypeItemOffset, charBuffer),
classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer));
break;
case Symbol.CONSTANT_DYNAMIC_TAG:
case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
nameAndTypeItemOffset =
classReader.getItem(classReader.readUnsignedShort(itemOffset + 2));
addConstantInvokeDynamic(
addConstantBootstrapReference(
itemTag,
itemIndex,
classReader.readUTF8(nameAndTypeItemOffset, charBuffer),
classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer),
......@@ -498,6 +500,10 @@ final class SymbolTable {
Handle handle = (Handle) value;
return addConstantMethodHandle(
handle.tag, handle.owner, handle.name, handle.descriptor, handle.isInterface);
} else if (value instanceof Condy) {
Condy condy = (Condy) value;
return addConstantDynamic(
condy.name, condy.descriptor, condy.bootstrapMethod, condy.bootstrapArguments);
} else {
throw new IllegalArgumentException("value " + value);
}
......@@ -865,6 +871,27 @@ final class SymbolTable {
return addConstantUtf8Reference(Symbol.CONSTANT_METHOD_TYPE_TAG, methodDescriptor);
}
/**
* Adds a CONSTANT_Dynamic_info to the constant pool of this symbol table. Also adds the related
* bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the constant
* pool already contains a similar item.
*
* @param name a method name.
* @param descriptor a field descriptor.
* @param bootstrapMethodHandle a bootstrap method handle.
* @param bootstrapMethodArguments the bootstrap method arguments.
* @return a new or already existing Symbol with the given value.
*/
Symbol addConstantDynamic(
final String name,
final String descriptor,
final Handle bootstrapMethodHandle,
final Object... bootstrapMethodArguments) {
Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments);
return addConstantBootstrapReference(
Symbol.CONSTANT_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index);
}
/**
* Adds a CONSTANT_InvokeDynamic_info to the constant pool of this symbol table. Also adds the
* related bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the
......@@ -882,21 +909,24 @@ final class SymbolTable {
final Handle bootstrapMethodHandle,
final Object... bootstrapMethodArguments) {
Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments);
return addConstantInvokeDynamic(name, descriptor, bootstrapMethod.index);
return addConstantBootstrapReference(
Symbol.CONSTANT_INVOKE_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index);
}
/**
* Adds a CONSTANT_InvokeDynamic_info to the constant pool of this symbol table. Does nothing if
* the constant pool already contains a similar item.
* Adds a CONSTANT_Dynamic or a CONSTANT_InvokeDynamic_info to the constant pool of this symbol
* table. Does nothing if the constant pool already contains a similar item.
*
* @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link
* Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}.
* @param name a method name.
* @param descriptor a method descriptor.
* @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG) or a method descriptor for
* CONSTANT_INVOKE_DYNAMIC_TAG.
* @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute.
* @return a new or already existing Symbol with the given value.
*/
private Symbol addConstantInvokeDynamic(
final String name, final String descriptor, final int bootstrapMethodIndex) {
final int tag = Symbol.CONSTANT_INVOKE_DYNAMIC_TAG;
private Symbol addConstantBootstrapReference(
final int tag, final String name, final String descriptor, final int bootstrapMethodIndex) {
int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex);
Entry entry = get(hashCode);
while (entry != null) {
......@@ -916,16 +946,23 @@ final class SymbolTable {
}
/**
* Adds a new CONSTANT_InvokeDynamic_info to the constant pool of this symbol table.
* Adds a new CONSTANT_Dynamic_info or CONSTANT_InvokeDynamic_info to the constant pool of this
* symbol table.
*
* @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link
* Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}.
* @param index the constant pool index of the new Symbol.
* @param name a method name.
* @param descriptor a method descriptor.
* @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG) or a method descriptor for
* CONSTANT_INVOKE_DYNAMIC_TAG.
* @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute.
*/
private void addConstantInvokeDynamic(
final int index, final String name, final String descriptor, final int bootstrapMethodIndex) {
final int tag = Symbol.CONSTANT_INVOKE_DYNAMIC_TAG;
private void addConstantBootstrapReference(
final int index,
final int tag,
final String name,
final String descriptor,
final int bootstrapMethodIndex) {
int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex);
add(new Entry(index, tag, null, name, descriptor, bootstrapMethodIndex, hashCode));
}
......@@ -1011,6 +1048,15 @@ final class SymbolTable {
bootstrapMethodsAttribute = bootstrapMethods = new ByteVector();
}
// CONDY can be recursive (and INDY can have CONDY bootstrap method arguments)
// so reserve them as constant pool constant first so the call to
// addConstant() when writing will only use already existing constants
// This loop can also stackoverflow if a CONDY reference itself,
// let burn into flame at that point
for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
addConstant(bootstrapMethodArgument);
}
// Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to
// compare it with existing ones, and will be reverted below if there is already a similar
// bootstrap method.
......@@ -1025,15 +1071,17 @@ final class SymbolTable {
.index);
int numBootstrapArguments = bootstrapMethodArguments.length;
bootstrapMethodsAttribute.putShort(numBootstrapArguments);
for (int i = 0; i < numBootstrapArguments; i++) {
bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArguments[i]).index);
for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index);
}
// Compute the length and the hash code of the bootstrap method.
// TODO Eric: should we merge this loop with the one that preallocate
// the bootstrap constants
int bootstrapMethodlength = bootstrapMethodsAttribute.length - bootstrapMethodOffset;
int hashCode = bootstrapMethodHandle.hashCode();
for (int i = 0; i < numBootstrapArguments; i++) {
hashCode ^= bootstrapMethodArguments[i].hashCode();
for (Object bootstrapMethodArgument : bootstrapMethodArguments) {