8294980: test/jdk/java/lang/invoke 15 test classes use experimental bytecode library
Reviewed-by: asotona
This commit is contained in:
parent
e841897247
commit
7bc8e4c891
@ -23,121 +23,102 @@
|
||||
|
||||
package test.java.lang.invoke.lib;
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.BasicTypeHelper;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.PoolHelper;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import jdk.internal.classfile.ClassBuilder;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.TypeKind;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.invoke.MethodType.fromMethodDescriptorString;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class InstructionHelper {
|
||||
|
||||
static final BasicTypeHelper BTH = new BasicTypeHelper();
|
||||
|
||||
static final AtomicInteger COUNT = new AtomicInteger();
|
||||
|
||||
static BasicClassBuilder classBuilder(MethodHandles.Lookup l) {
|
||||
String className = l.lookupClass().getCanonicalName().replace('.', '/') + "$Code_" + COUNT.getAndIncrement();
|
||||
return new BasicClassBuilder(className, 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
));
|
||||
private static void commonBuild(ClassBuilder classBuilder) {
|
||||
classBuilder
|
||||
.withVersion(55, 0)
|
||||
.withSuperclass(ConstantDescs.CD_Object)
|
||||
.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, Classfile.ACC_PUBLIC,
|
||||
methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.aload(0)
|
||||
.invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME,
|
||||
ConstantDescs.MTD_void, false)
|
||||
.return_()));
|
||||
}
|
||||
|
||||
public static MethodHandle invokedynamic(MethodHandles.Lookup l,
|
||||
String name, MethodType type,
|
||||
String bsmMethodName, MethodType bsmType,
|
||||
Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
|
||||
byte[] byteArray = classBuilder(l)
|
||||
.withMethod("m", type.toMethodDescriptorString(), M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new,
|
||||
C -> {
|
||||
for (int i = 0; i < type.parameterCount(); i++) {
|
||||
C.load(BTH.tag(cref(type.parameterType(i))), i);
|
||||
}
|
||||
C.invokedynamic(name, type.toMethodDescriptorString(),
|
||||
csym(l.lookupClass()), bsmMethodName, bsmType.toMethodDescriptorString(),
|
||||
staticArgs);
|
||||
C.return_(BTH.tag(cref(type.returnType())));
|
||||
}
|
||||
))
|
||||
.build();
|
||||
public static MethodHandle invokedynamic(MethodHandles.Lookup l, String name, MethodType type, String bsmMethodName,
|
||||
MethodType bsmType, ConstantDesc... boostrapArgs) throws Exception {
|
||||
ClassDesc genClassDesc = classDesc(l.lookupClass(), "$Code_" + COUNT.getAndIncrement());
|
||||
byte[] byteArray = Classfile.of().build(genClassDesc, classBuilder -> {
|
||||
commonBuild(classBuilder);
|
||||
classBuilder
|
||||
.withMethod("m", MethodTypeDesc.ofDescriptor(type.toMethodDescriptorString()),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> {
|
||||
for (int i = 0; i < type.parameterCount(); i++) {
|
||||
codeBuilder.loadInstruction(TypeKind.from(type.parameterType(i)), i);
|
||||
}
|
||||
codeBuilder.invokedynamic(DynamicCallSiteDesc.of(
|
||||
MethodHandleDesc.ofMethod(
|
||||
DirectMethodHandleDesc.Kind.STATIC,
|
||||
classDesc(l.lookupClass()),
|
||||
bsmMethodName,
|
||||
MethodTypeDesc.ofDescriptor(
|
||||
bsmType.toMethodDescriptorString())),
|
||||
name,
|
||||
MethodTypeDesc.ofDescriptor(type.toMethodDescriptorString()),
|
||||
boostrapArgs));
|
||||
codeBuilder.returnInstruction(TypeKind.from(type.returnType()));
|
||||
}));
|
||||
});
|
||||
Class<?> gc = l.defineClass(byteArray);
|
||||
return l.findStatic(gc, "m", type);
|
||||
}
|
||||
|
||||
public static MethodHandle ldcMethodHandle(MethodHandles.Lookup l,
|
||||
int refKind, Class<?> owner, String name, MethodType type) throws Exception {
|
||||
return ldc(l, MethodHandle.class,
|
||||
P -> P.putHandle(refKind, csym(owner), name, type.toMethodDescriptorString()));
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l, String name, Class<?> type, String bsmMethodName,
|
||||
MethodType bsmType, ConstantDesc... bootstrapArgs) throws Exception {
|
||||
return ldcDynamicConstant(l, name, type, l.lookupClass(), bsmMethodName, bsmType, bootstrapArgs);
|
||||
}
|
||||
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
|
||||
String name, Class<?> type,
|
||||
String bsmMethodName, MethodType bsmType,
|
||||
Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
|
||||
return ldcDynamicConstant(l, name, type, l.lookupClass(), bsmMethodName, bsmType, staticArgs);
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l, String name, Class<?> type, Class<?> bsmClass,
|
||||
String bsmMethodName, MethodType bsmType, ConstantDesc... bootstrapArgs) throws Exception {
|
||||
return ldcDynamicConstant(l, name, type.descriptorString(), bsmClass.descriptorString(), bsmMethodName,
|
||||
bsmType.descriptorString(), bootstrapArgs);
|
||||
}
|
||||
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
|
||||
String name, Class<?> type,
|
||||
Class<?> bsmClass, String bsmMethodName, MethodType bsmType,
|
||||
Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
|
||||
return ldcDynamicConstant(l, name, cref(type), csym(bsmClass), bsmMethodName, bsmType.toMethodDescriptorString(), staticArgs);
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l, String name, String type, String bsmMethodName,
|
||||
String bsmType, ConstantDesc... bootstrapArgs) throws Exception {
|
||||
return ldcDynamicConstant(l, name, type, l.lookupClass().descriptorString(), bsmMethodName, bsmType, bootstrapArgs);
|
||||
}
|
||||
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
|
||||
String name, String type,
|
||||
String bsmMethodName, String bsmType,
|
||||
Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
|
||||
return ldcDynamicConstant(l, name, type, csym(l.lookupClass()), bsmMethodName, bsmType, staticArgs);
|
||||
}
|
||||
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
|
||||
String name, String type,
|
||||
String bsmClass, String bsmMethodName, String bsmType,
|
||||
Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
|
||||
return ldc(l, type,
|
||||
P -> P.putDynamicConstant(name, type,
|
||||
bsmClass, bsmMethodName, bsmType,
|
||||
staticArgs));
|
||||
}
|
||||
|
||||
public static MethodHandle ldc(MethodHandles.Lookup l,
|
||||
Class<?> type,
|
||||
Function<PoolHelper<String, String, byte[]>, Integer> poolFunc) throws Exception {
|
||||
return ldc(l, cref(type), poolFunc);
|
||||
}
|
||||
|
||||
public static MethodHandle ldc(MethodHandles.Lookup l,
|
||||
String type,
|
||||
Function<PoolHelper<String, String, byte[]>, Integer> poolFunc) throws Exception {
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l, String name, String type, String bsmClass,
|
||||
String bsmMethodName, String bsmType, ConstantDesc... bootstrapArgs)
|
||||
throws IllegalAccessException, NoSuchMethodException {
|
||||
String methodType = "()" + type;
|
||||
byte[] byteArray = classBuilder(l)
|
||||
.withMethod("m", "()" + type, M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new,
|
||||
C -> {
|
||||
C.ldc(null, (P, v) -> poolFunc.apply(P));
|
||||
C.return_(BTH.tag(type));
|
||||
}
|
||||
))
|
||||
.build();
|
||||
Class<?> gc = l.defineClass(byteArray);
|
||||
ClassDesc genClassDesc = classDesc(l.lookupClass(), "$Code_" + COUNT.getAndIncrement());
|
||||
byte[] bytes = Classfile.of().build(genClassDesc, classBuilder -> {
|
||||
commonBuild(classBuilder);
|
||||
classBuilder.withMethod("m", MethodTypeDesc.of(ClassDesc.ofDescriptor(type)),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
MethodHandleDesc.ofMethod(
|
||||
DirectMethodHandleDesc.Kind.STATIC,
|
||||
ClassDesc.ofDescriptor(bsmClass),
|
||||
bsmMethodName,
|
||||
MethodTypeDesc.ofDescriptor(bsmType)),
|
||||
name,
|
||||
ClassDesc.ofDescriptor(type),
|
||||
bootstrapArgs))
|
||||
.returnInstruction(TypeKind.fromDescriptor(type))));
|
||||
});
|
||||
Class<?> gc = l.defineClass(bytes);
|
||||
return l.findStatic(gc, "m", fromMethodDescriptorString(methodType, l.lookupClass().getClassLoader()));
|
||||
}
|
||||
|
||||
@ -145,7 +126,13 @@ public class InstructionHelper {
|
||||
return c.getCanonicalName().replace('.', '/');
|
||||
}
|
||||
|
||||
public static String cref(Class<?> c) {
|
||||
return methodType(c).toMethodDescriptorString().substring(2);
|
||||
public static ClassDesc classDesc(Class<?> c) {
|
||||
return ClassDesc.ofDescriptor(c.descriptorString());
|
||||
}
|
||||
|
||||
public static ClassDesc classDesc(Class<?> c, String suffix) {
|
||||
StringBuilder sb = new StringBuilder(c.descriptorString());
|
||||
String classDescStr = sb.insert(sb.length() - 1, suffix).toString();
|
||||
return ClassDesc.ofDescriptor(classDescStr);
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,17 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test bootstrap methods throwing an exception
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng BootstrapMethodJumboArgsTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 BootstrapMethodJumboArgsTest
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.PoolHelper;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
@ -49,12 +53,11 @@ public class BootstrapMethodJumboArgsTest {
|
||||
|
||||
|
||||
static Object bsmZero(MethodHandles.Lookup l, String name, Object type,
|
||||
Object... args) {
|
||||
Object... args) {
|
||||
Object[] a = args.clone();
|
||||
if (type instanceof MethodType) {
|
||||
return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@ -66,8 +69,7 @@ public class BootstrapMethodJumboArgsTest {
|
||||
System.arraycopy(args, 0, a, 1, args.length);
|
||||
if (type instanceof MethodType) {
|
||||
return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@ -80,18 +82,11 @@ public class BootstrapMethodJumboArgsTest {
|
||||
System.arraycopy(args, 0, a, 2, args.length);
|
||||
if (type instanceof MethodType) {
|
||||
return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
static void manyStaticStrings(String[] args, PoolHelper.StaticArgListBuilder<String, String, byte[]> staticArgs) {
|
||||
for (String s : args) {
|
||||
staticArgs.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCondyWithJumboArgs() throws Throwable {
|
||||
String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new);
|
||||
@ -99,8 +94,8 @@ public class BootstrapMethodJumboArgsTest {
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object[].class,
|
||||
"bsmZero", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
"bsmZero", methodType(Object.class, MethodHandles.Lookup.class, String.class,
|
||||
Object.class, Object[].class), expected);
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
@ -109,8 +104,8 @@ public class BootstrapMethodJumboArgsTest {
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object[].class,
|
||||
"bsmOne", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
"bsmOne", methodType(Object.class, MethodHandles.Lookup.class, String.class,
|
||||
Object.class, Object.class, Object[].class), expected);
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
@ -119,8 +114,8 @@ public class BootstrapMethodJumboArgsTest {
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object[].class,
|
||||
"bsmTwo", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
"bsmTwo", methodType(Object.class, MethodHandles.Lookup.class, String.class,
|
||||
Object.class, Object.class, Object.class, Object[].class), expected);
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
@ -134,8 +129,8 @@ public class BootstrapMethodJumboArgsTest {
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.invokedynamic(
|
||||
L, "name", methodType(Object[].class),
|
||||
"bsmZero", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
"bsmZero", methodType(Object.class, MethodHandles.Lookup.class, String.class,
|
||||
Object.class, Object[].class), expected);
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
@ -144,8 +139,8 @@ public class BootstrapMethodJumboArgsTest {
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.invokedynamic(
|
||||
L, "name", methodType(Object[].class),
|
||||
"bsmOne", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
"bsmOne", methodType(Object.class, MethodHandles.Lookup.class, String.class,
|
||||
Object.class, Object.class, Object[].class), expected);
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
@ -154,8 +149,8 @@ public class BootstrapMethodJumboArgsTest {
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.invokedynamic(
|
||||
L, "name", methodType(Object[].class),
|
||||
"bsmTwo", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
"bsmTwo", methodType(Object.class, MethodHandles.Lookup.class, String.class,
|
||||
Object.class, Object.class, Object.class, Object[].class), expected);
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
|
@ -25,8 +25,13 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test bootstrap methods throwing an exception
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyBSMException
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMException
|
||||
*/
|
||||
@ -64,7 +69,7 @@ public class CondyBSMException {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testException() throws Throwable {
|
||||
public void testException() {
|
||||
test("Exception", BootstrapMethodError.class, Exception.class);
|
||||
}
|
||||
|
||||
@ -73,8 +78,7 @@ public class CondyBSMException {
|
||||
Throwable caught = null;
|
||||
try {
|
||||
mh.invoke();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
} catch (Throwable t) {
|
||||
caught = t;
|
||||
}
|
||||
|
||||
@ -98,8 +102,7 @@ public class CondyBSMException {
|
||||
try {
|
||||
Constructor<Throwable> c = type.getDeclaredConstructor(String.class);
|
||||
t = c.newInstance(name);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
throw t;
|
||||
@ -110,8 +113,9 @@ public class CondyBSMException {
|
||||
return InstructionHelper.ldcDynamicConstant(
|
||||
MethodHandles.lookup(),
|
||||
message, t,
|
||||
"throwingBsm", methodType(Throwable.class, MethodHandles.Lookup.class, String.class, Class.class),
|
||||
S -> { });
|
||||
"throwingBsm",
|
||||
methodType(Throwable.class, MethodHandles.Lookup.class, String.class, Class.class)
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -25,8 +25,13 @@
|
||||
* @test
|
||||
* @bug 8186046 8199875
|
||||
* @summary Test basic invocation of bootstrap methods
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyBSMInvocation
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMInvocation
|
||||
*/
|
||||
@ -36,6 +41,7 @@ import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.constant.ConstantDesc;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@ -55,8 +61,8 @@ public class CondyBSMInvocation {
|
||||
public void testNonexistent() throws Throwable {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"bsm", methodType(Object.class),
|
||||
S -> {});
|
||||
"bsm", methodType(Object.class)
|
||||
);
|
||||
|
||||
try {
|
||||
mh.invoke();
|
||||
@ -110,8 +116,7 @@ public class CondyBSMInvocation {
|
||||
for (MethodHandle bsm : bsms("shape_bsm")) {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"shape_bsm", bsm.type(),
|
||||
S -> {}
|
||||
"shape_bsm", bsm.type()
|
||||
);
|
||||
|
||||
try {
|
||||
@ -136,8 +141,7 @@ public class CondyBSMInvocation {
|
||||
for (MethodHandle bsm : bsms("sig_bsm")) {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"sig_bsm", bsm.type(),
|
||||
S -> {}
|
||||
"sig_bsm", bsm.type()
|
||||
);
|
||||
|
||||
try {
|
||||
@ -216,8 +220,8 @@ public class CondyBSMInvocation {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"bsm", mt,
|
||||
S -> IntStream.range(0, n).forEach(S::add)
|
||||
);
|
||||
IntStream.range(0, n).boxed().toArray(ConstantDesc[]::new)
|
||||
);
|
||||
|
||||
Object r = mh.invoke();
|
||||
Assert.assertEquals(r, Integer.toString(n));
|
||||
@ -228,7 +232,7 @@ public class CondyBSMInvocation {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"bsm", mt,
|
||||
S -> IntStream.range(0, 9).forEach(S::add)
|
||||
IntStream.range(0, 9).boxed().toArray(ConstantDesc[]::new)
|
||||
);
|
||||
|
||||
Object r = mh.invoke();
|
||||
@ -246,7 +250,7 @@ public class CondyBSMInvocation {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"bsm", mt,
|
||||
S -> IntStream.range(0, n - 1).forEach(S::add)
|
||||
IntStream.range(0, n - 1).boxed().toArray(ConstantDesc[]::new)
|
||||
);
|
||||
|
||||
try {
|
||||
|
@ -25,12 +25,18 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test invalid name in name and type
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyBSMValidationTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMValidationTest
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
@ -49,19 +55,22 @@ public class CondyBSMValidationTest {
|
||||
@DataProvider
|
||||
public Object[][] invalidSignaturesProvider() throws Exception {
|
||||
return Stream.of(BSM_TYPE.replace("(", ""),
|
||||
BSM_TYPE.replace(")", ""),
|
||||
BSM_TYPE.replace("(", "").replace(")", ""),
|
||||
BSM_TYPE.replace(";)", ")"),
|
||||
BSM_TYPE.replace(";", ""))
|
||||
BSM_TYPE.replace(")", ""),
|
||||
BSM_TYPE.replace("(", "").replace(")", ""),
|
||||
BSM_TYPE.replace(";)", ")"),
|
||||
BSM_TYPE.replace(";", ""))
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "invalidSignaturesProvider", expectedExceptions = ClassFormatError.class)
|
||||
@Test(dataProvider = "invalidSignaturesProvider")
|
||||
public void testInvalidBSMSignature(String sig) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", "Ljava/lang/Object;",
|
||||
"bsm", sig,
|
||||
S -> {
|
||||
});
|
||||
try {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", "Ljava/lang/Object;",
|
||||
"bsm", sig
|
||||
);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertTrue(e.getMessage().contains("Bad method descriptor"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,18 +25,23 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test for an interface using condy with default overpass methods
|
||||
* @library /lib/testlibrary/bytecode
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyInterfaceWithOverpassMethods
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyInterfaceWithOverpassMethods
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
@ -70,24 +75,32 @@ public class CondyInterfaceWithOverpassMethods {
|
||||
Class<?> thisClass = CondyInterfaceWithOverpassMethods.class;
|
||||
|
||||
String genClassName = thisClass.getSimpleName() + "$Code";
|
||||
String bsmClassName = thisClass.getCanonicalName().replace('.', '/');
|
||||
String bsmClassName = thisClass.descriptorString();
|
||||
String bsmMethodName = "bsm";
|
||||
String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class,
|
||||
String.class, Class.class).toMethodDescriptorString();
|
||||
|
||||
byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
|
||||
.withFlags(Flag.ACC_INTERFACE, Flag.ACC_ABSTRACT)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withSuperinterface(thisClass.getCanonicalName().replace('.', '/') + "$" + A.class.getSimpleName())
|
||||
.withMethod("y", "()Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("String", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> {})
|
||||
.areturn()
|
||||
))
|
||||
.build();
|
||||
String.class, Class.class).toMethodDescriptorString();
|
||||
|
||||
byte[] byteArray = Classfile.of().build(ClassDesc.of(genClassName), classBuilder -> classBuilder
|
||||
.withFlags(Classfile.ACC_INTERFACE + Classfile.ACC_ABSTRACT)
|
||||
.withSuperclass(ConstantDescs.CD_Object)
|
||||
.withInterfaceSymbols(InstructionHelper.classDesc(A.class))
|
||||
.withMethod("y", MethodTypeDesc.of(ConstantDescs.CD_String), Classfile.ACC_PUBLIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
MethodHandleDesc.of(
|
||||
DirectMethodHandleDesc.Kind.STATIC,
|
||||
ClassDesc.ofDescriptor(bsmClassName),
|
||||
bsmMethodName,
|
||||
bsmDescriptor
|
||||
),
|
||||
"String",
|
||||
ConstantDescs.CD_String
|
||||
)
|
||||
)
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
);
|
||||
gc = MethodHandles.lookup().defineClass(byteArray);
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,18 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test invalid name in name and type
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyNameValidationTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNameValidationTest
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
@ -47,29 +53,33 @@ public class CondyNameValidationTest {
|
||||
static final MethodType BSM_TYPE = methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class);
|
||||
|
||||
@DataProvider
|
||||
public Object[][] invalidNamesProvider() throws Exception {
|
||||
return Stream.of("",
|
||||
".",
|
||||
";",
|
||||
"[",
|
||||
"/")
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
public Object[][] invalidNamesProvider() {
|
||||
return Stream.of(
|
||||
new Object[]{"", "zero-length member name"},
|
||||
new Object[]{".", "Invalid member name"},
|
||||
new Object[]{";", "Invalid member name"},
|
||||
new Object[]{"[", "Invalid member name"},
|
||||
new Object[]{"/", "Invalid member name"}
|
||||
)
|
||||
.toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "invalidNamesProvider", expectedExceptions = java.lang.ClassFormatError.class)
|
||||
public void testInvalidNames(String name) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, name, Object.class,
|
||||
"bsm", BSM_TYPE,
|
||||
S -> {
|
||||
});
|
||||
@Test(dataProvider = "invalidNamesProvider")
|
||||
public void testInvalidNames(String name, String errMessContent) throws Exception {
|
||||
try {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, name, Object.class,
|
||||
"bsm", BSM_TYPE
|
||||
);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertTrue(e.getMessage().contains(errMessContent));
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public Object[][] validNamesProvider() throws Exception {
|
||||
return Stream.of("<clinit>",
|
||||
"<init>",
|
||||
"<foo>")
|
||||
"<init>")
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@ -77,8 +87,7 @@ public class CondyNameValidationTest {
|
||||
public void testValidNames(String name) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, name, Object.class,
|
||||
"bsm", BSM_TYPE,
|
||||
S -> {
|
||||
});
|
||||
"bsm", BSM_TYPE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,11 @@
|
||||
* @bug 8186046
|
||||
* @summary Test nested dynamic constant declarations that are recursive
|
||||
* @compile CondyNestedTest_Code.jcod
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyNestedTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNestedTest
|
||||
*/
|
||||
@ -34,21 +39,17 @@ import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class CondyNestedTest {
|
||||
|
||||
static final Class[] THROWABLES = {InvocationTargetException.class, StackOverflowError.class};
|
||||
private static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
|
||||
Class<?> c;
|
||||
|
||||
// Add the following annotations to the test description if uncommenting the
|
||||
// following code
|
||||
//
|
||||
// * @library /lib/testlibrary/bytecode
|
||||
// * @build jdk.experimental.bytecode.BasicClassBuilder
|
||||
//
|
||||
// static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
//
|
||||
// /**
|
||||
@ -65,97 +66,178 @@ public class CondyNestedTest {
|
||||
// */
|
||||
// public static byte[] generator() throws Exception {
|
||||
// String genClassName = L.lookupClass().getSimpleName() + "_Code";
|
||||
// String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class).toMethodDescriptorString();
|
||||
// String bsmIndyDescriptor = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class).toMethodDescriptorString();
|
||||
// ClassDesc genClassDesc = ClassDesc.of(genClassName);
|
||||
// String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class,
|
||||
// Object.class).toMethodDescriptorString();
|
||||
// String bsmIndyDescriptor = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
|
||||
// Object.class, Object.class).toMethodDescriptorString();
|
||||
// DirectMethodHandleDesc bsmMhDesc = MethodHandleDesc.of(
|
||||
// DirectMethodHandleDesc.Kind.STATIC,
|
||||
// genClassDesc,
|
||||
// "bsm",
|
||||
// bsmDescriptor
|
||||
// );
|
||||
// DirectMethodHandleDesc bsmIndyMhDesc = MethodHandleDesc.of(
|
||||
// DirectMethodHandleDesc.Kind.STATIC,
|
||||
// genClassDesc,
|
||||
// "bsmIndy",
|
||||
// bsmIndyDescriptor
|
||||
// );
|
||||
// byte[] byteArray = Classfile.of().build(ClassDesc.of(genClassName), classBuilder -> classBuilder
|
||||
// .withVersion(55, 0)
|
||||
// .withSuperclass(ConstantDescs.CD_Object)
|
||||
// .withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, Classfile.ACC_PUBLIC, methodBuilder -> methodBuilder
|
||||
// .withCode(codeBuilder -> codeBuilder
|
||||
// .aload(0)
|
||||
// .invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME, ConstantDescs.MTD_void)
|
||||
// .return_()
|
||||
// )
|
||||
// )
|
||||
// .withMethod("main", MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_String.arrayType()),
|
||||
// Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
// .withCode(codeBuilder -> {
|
||||
// codeBuilder
|
||||
// .aload(0)
|
||||
// .iconst_0()
|
||||
// .aaload()
|
||||
// .invokevirtual(ConstantDescs.CD_String, "intern",
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_String))
|
||||
// .astore(1);
|
||||
// Label case1 = codeBuilder.newLabel();
|
||||
// codeBuilder
|
||||
// .aload(1)
|
||||
// .ldc("condy_bsm_condy_bsm")
|
||||
// .if_acmpne(case1)
|
||||
// .invokestatic(genClassDesc, "condy_bsm_condy_bsm",
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_Object))
|
||||
// .return_();
|
||||
// Label case2 = codeBuilder.newLabel();
|
||||
// codeBuilder
|
||||
// .labelBinding(case1)
|
||||
// .aload(1)
|
||||
// .ldc("indy_bsmIndy_condy_bsm")
|
||||
// .if_acmpne(case2)
|
||||
// .invokestatic(genClassDesc, "indy_bsmIndy_condy_bsm",
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_Object))
|
||||
// .return_();
|
||||
// Label case3 = codeBuilder.newLabel();
|
||||
// codeBuilder
|
||||
// .labelBinding(case2)
|
||||
// .aload(1)
|
||||
// .ldc("indy_bsm_condy_bsm")
|
||||
// .if_acmpne(case3)
|
||||
// .invokestatic(genClassDesc, "indy_bsm_condy_bsm",
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_Object))
|
||||
// .return_();
|
||||
// codeBuilder
|
||||
// .labelBinding(case3)
|
||||
// .return_();
|
||||
// }
|
||||
// )
|
||||
// )
|
||||
// // bsm that when used with indy returns a call site whose target is MethodHandles.constant(String.class, name), and
|
||||
// // when used with condy returns the name
|
||||
// .withMethod("bsm", MethodTypeDesc.ofDescriptor(bsmDescriptor),
|
||||
// Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
// .withCode(codeBuilder -> {
|
||||
// codeBuilder
|
||||
// .aload(2)
|
||||
// .instanceof_(ConstantDescs.CD_MethodType)
|
||||
// .iconst_0();
|
||||
// Label condy = codeBuilder.newLabel();
|
||||
// codeBuilder
|
||||
// .if_acmpeq(condy)
|
||||
// .new_(ClassDesc.ofDescriptor(ConstantCallSite.class.descriptorString()))
|
||||
// .dup()
|
||||
// .ldc(ConstantDescs.CD_String)
|
||||
// .aload(1)
|
||||
// .invokestatic(ConstantDescs.CD_MethodHandles, "constant",
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_MethodHandle, ConstantDescs.CD_Class,
|
||||
// ConstantDescs.CD_Object))
|
||||
// .invokespecial(ClassDesc.ofDescriptor(ConstantCallSite.class.descriptorString()),
|
||||
// ConstantDescs.INIT_NAME,
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_MethodHandle))
|
||||
// .areturn();
|
||||
// codeBuilder
|
||||
// .labelBinding(condy)
|
||||
// .aload(1)
|
||||
// .areturn();
|
||||
// }
|
||||
//
|
||||
// byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
|
||||
// .withSuperclass("java/lang/Object")
|
||||
// .withMethod("<init>", "()V", M ->
|
||||
// M.withFlags(Flag.ACC_PUBLIC)
|
||||
// .withCode(TypedCodeBuilder::new, C ->
|
||||
// C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
// ))
|
||||
// .withMethod("main", "([Ljava/lang/String;)V", M ->
|
||||
// M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
// .withCode(TypedCodeBuilder::new, C -> {
|
||||
// C.aload_0().iconst_0().aaload();
|
||||
// C.invokevirtual("java/lang/String", "intern", "()Ljava/lang/String;", false);
|
||||
// C.astore_1();
|
||||
//
|
||||
// C.aload_1();
|
||||
// C.ldc("condy_bsm_condy_bsm");
|
||||
// C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE1");
|
||||
// C.invokestatic(genClassName, "condy_bsm_condy_bsm", "()Ljava/lang/Object;", false).return_();
|
||||
//
|
||||
// C.label("CASE1");
|
||||
// C.aload_1();
|
||||
// C.ldc("indy_bsmIndy_condy_bsm");
|
||||
// C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE2");
|
||||
// C.invokestatic(genClassName, "indy_bsmIndy_condy_bsm", "()Ljava/lang/Object;", false).return_();
|
||||
//
|
||||
// C.label("CASE2");
|
||||
// C.aload_1();
|
||||
// C.ldc("indy_bsm_condy_bsm");
|
||||
// C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE3");
|
||||
// C.invokestatic(genClassName, "indy_bsm_condy_bsm", "()Ljava/lang/Object;", false).return_();
|
||||
//
|
||||
// C.label("CASE3");
|
||||
// C.return_();
|
||||
// }))
|
||||
// .withMethod("bsm", bsmDescriptor, M ->
|
||||
// M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
// .withCode(TypedCodeBuilder::new, C -> {
|
||||
// C.aload_2();
|
||||
// C.instanceof_("java/lang/invoke/MethodType");
|
||||
// C.iconst_0();
|
||||
// C.ifcmp(TypeTag.I, MacroCodeBuilder.CondKind.EQ, "CONDY");
|
||||
// C.new_("java/lang/invoke/ConstantCallSite").dup();
|
||||
// C.ldc("java/lang/String", PoolHelper::putClass);
|
||||
// C.aload_1();
|
||||
// C.invokestatic("java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false);
|
||||
// C.invokespecial("java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
|
||||
// C.areturn();
|
||||
// C.label("CONDY");
|
||||
// C.aload_1().areturn();
|
||||
// }))
|
||||
// .withMethod("bsmIndy", bsmIndyDescriptor, M ->
|
||||
// M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
// .withCode(TypedCodeBuilder::new, C -> {
|
||||
// C.new_("java/lang/invoke/ConstantCallSite").dup();
|
||||
// C.ldc("java/lang/String", PoolHelper::putClass);
|
||||
// C.aload_1();
|
||||
// C.invokestatic("java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false);
|
||||
// C.invokespecial("java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
|
||||
// C.areturn();
|
||||
// }))
|
||||
// .withMethod("condy_bsm_condy_bsm", "()Ljava/lang/Object;", M ->
|
||||
// M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
// .withCode(TypedCodeBuilder::new, C ->
|
||||
// C.ldc("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
// S -> S.add(null, (P, v) -> {
|
||||
// return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
// S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
|
||||
// }))
|
||||
// .areturn()))
|
||||
// .withMethod("indy_bsmIndy_condy_bsm", "()Ljava/lang/Object;", M ->
|
||||
// M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
// .withCode(TypedCodeBuilder::new, C ->
|
||||
// C.invokedynamic("name", "()Ljava/lang/String;", genClassName, "bsmIndy", bsmIndyDescriptor,
|
||||
// S -> S.add(null, (P, v) -> {
|
||||
// return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
// S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
|
||||
// }))
|
||||
// .areturn()))
|
||||
// .withMethod("indy_bsm_condy_bsm", "()Ljava/lang/Object;", M ->
|
||||
// M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
// .withCode(TypedCodeBuilder::new, C ->
|
||||
// C.invokedynamic("name", "()Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
// S -> S.add(null, (P, v) -> {
|
||||
// return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
// S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
|
||||
// }))
|
||||
// .areturn()))
|
||||
// .build();
|
||||
// )
|
||||
// )
|
||||
// // an indy bsm, that returns a call site whose target is MethodHandles.constant(String.class, methodName)
|
||||
// .withMethod("bsmIndy", MethodTypeDesc.ofDescriptor(bsmIndyDescriptor),
|
||||
// Classfile.ACC_PUBLIC + Classfile.ACC_PUBLIC, methodBuilder -> methodBuilder
|
||||
// .withCode(codeBuilder -> codeBuilder
|
||||
// .new_(ClassDesc.ofDescriptor(ConstantCallSite.class.descriptorString()))
|
||||
// .dup()
|
||||
// .ldc(ConstantDescs.CD_String)
|
||||
// .aload(1)
|
||||
// .invokestatic(ConstantDescs.CD_MethodHandles, "constant",
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_MethodHandle, ConstantDescs.CD_Class,
|
||||
// ConstantDescs.CD_Object))
|
||||
// .invokespecial(ClassDesc.ofDescriptor(ConstantCallSite.class.descriptorString()),
|
||||
// ConstantDescs.INIT_NAME,
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_MethodHandle))
|
||||
// .areturn()
|
||||
// )
|
||||
// )
|
||||
// .withMethod("condy_bsm_condy_bsm", MethodTypeDesc.of(ConstantDescs.CD_Object),
|
||||
// Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
// .withCode(codeBuilder -> codeBuilder
|
||||
// .ldc(DynamicConstantDesc.ofNamed(
|
||||
// bsmMhDesc,
|
||||
// "name",
|
||||
// ConstantDescs.CD_String,
|
||||
// DynamicConstantDesc.ofNamed(
|
||||
// bsmMhDesc,
|
||||
// "name",
|
||||
// ConstantDescs.CD_String,
|
||||
// "DUMMY_ARG"
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// .areturn()
|
||||
// )
|
||||
// )
|
||||
// .withMethod("indy_bsmIndy_condy_bsm", MethodTypeDesc.of(ConstantDescs.CD_Object),
|
||||
// Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
// .withCode(codeBuilder -> codeBuilder
|
||||
// .invokedynamic(DynamicCallSiteDesc.of(
|
||||
// bsmIndyMhDesc,
|
||||
// "name",
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_String),
|
||||
// DynamicConstantDesc.ofNamed(
|
||||
// bsmMhDesc,
|
||||
// "name",
|
||||
// ConstantDescs.CD_String,
|
||||
// "DUMMY_ARG"
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// .areturn()
|
||||
// )
|
||||
// )
|
||||
// .withMethod("indy_bsm_condy_bsm", MethodTypeDesc.of(ConstantDescs.CD_Object),
|
||||
// Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
// .withCode(codeBuilder -> codeBuilder
|
||||
// .invokedynamic(DynamicCallSiteDesc.of(
|
||||
// bsmMhDesc,
|
||||
// "name",
|
||||
// MethodTypeDesc.of(ConstantDescs.CD_String),
|
||||
// DynamicConstantDesc.ofNamed(
|
||||
// bsmMhDesc,
|
||||
// "name",
|
||||
// ConstantDescs.CD_String,
|
||||
// "DUMMY_ARG"
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// .areturn()
|
||||
// )
|
||||
// )
|
||||
// );
|
||||
//
|
||||
// File f = new File(genClassName + ".class");
|
||||
// if (f.getParentFile() != null) {
|
||||
@ -163,15 +245,13 @@ public class CondyNestedTest {
|
||||
// }
|
||||
// new FileOutputStream(f).write(byteArray);
|
||||
// return byteArray;
|
||||
//
|
||||
// }
|
||||
|
||||
static void test(Method m, Class<? extends Throwable>... ts) {
|
||||
Throwable caught = null;
|
||||
try {
|
||||
m.invoke(null);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
} catch (Throwable t) {
|
||||
caught = t;
|
||||
}
|
||||
|
||||
|
@ -25,19 +25,22 @@
|
||||
* @test
|
||||
* @bug 8186211
|
||||
* @summary Test basic invocation of multiple ldc's of the same dynamic constant that fail resolution
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder
|
||||
* @library /java/lang/invoke/common
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyRepeatFailedResolution
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyRepeatFailedResolution
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -95,124 +98,194 @@ public class CondyRepeatFailedResolution {
|
||||
@BeforeClass
|
||||
public void generateClass() throws Exception {
|
||||
String genClassName = CondyRepeatFailedResolution.class.getSimpleName() + "$Code";
|
||||
String bsmClassName = CondyRepeatFailedResolution.class.getCanonicalName().replace('.', '/');
|
||||
String bsmClassDesc = CondyRepeatFailedResolution.class.descriptorString();
|
||||
String bsmMethodName = "intConversion";
|
||||
String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class,
|
||||
String.class, Class.class, int.class).toMethodDescriptorString();
|
||||
|
||||
byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("B", "()B", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("B", "B", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Byte.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("C", "()C", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("C", "C", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Character.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("D", "()D", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("D", "D", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
))
|
||||
.withMethod("D_AsType", "()D", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "D", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
))
|
||||
.withMethod("F", "()F", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("F", "F", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
))
|
||||
.withMethod("F_AsType", "()F", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "F", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
))
|
||||
.withMethod("I", "()I", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "I", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("J", "()J", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("J", "J", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
))
|
||||
.withMethod("J_AsType", "()J", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "J", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
))
|
||||
.withMethod("S", "()S", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("S", "S", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Short.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("Z_F", "()Z", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(0))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("Z_T", "()Z", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(1))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("null", "()Ljava/lang/Object;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("nullRef", "Ljava/lang/Object;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.withMethod("string", "()Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("string", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.withMethod("stringArray", "()[Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("stringArray", "[Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.build();
|
||||
String.class, Class.class, int.class).toMethodDescriptorString();
|
||||
DirectMethodHandleDesc bsmMhDesc = MethodHandleDesc.of(
|
||||
DirectMethodHandleDesc.Kind.STATIC,
|
||||
ClassDesc.ofDescriptor(bsmClassDesc),
|
||||
bsmMethodName,
|
||||
bsmDescriptor
|
||||
);
|
||||
byte[] byteArray = Classfile.of().build(ClassDesc.of(genClassName), classBuilder -> classBuilder
|
||||
.withVersion(55, 0)
|
||||
.withSuperclass(ConstantDescs.CD_Object)
|
||||
.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, Classfile.ACC_PUBLIC,
|
||||
methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.aload(0)
|
||||
.invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME,
|
||||
ConstantDescs.MTD_void, false)
|
||||
.return_()
|
||||
)
|
||||
)
|
||||
.withMethod("B", MethodTypeDesc.of(ConstantDescs.CD_byte),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"B",
|
||||
ConstantDescs.CD_byte,
|
||||
(int) Byte.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("C", MethodTypeDesc.of(ConstantDescs.CD_char),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"C",
|
||||
ConstantDescs.CD_char,
|
||||
(int) Character.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("D", MethodTypeDesc.of(ConstantDescs.CD_double),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"D",
|
||||
ConstantDescs.CD_double,
|
||||
Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("D_AsType", MethodTypeDesc.of(ConstantDescs.CD_double),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_double,
|
||||
Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("F", MethodTypeDesc.of(ConstantDescs.CD_float),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"F",
|
||||
ConstantDescs.CD_float,
|
||||
Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
)
|
||||
)
|
||||
.withMethod("F_AsType", MethodTypeDesc.of(ConstantDescs.CD_float),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_float,
|
||||
Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
)
|
||||
)
|
||||
.withMethod("I", MethodTypeDesc.of(ConstantDescs.CD_int),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_int,
|
||||
Integer.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("J", MethodTypeDesc.of(ConstantDescs.CD_long),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"J",
|
||||
ConstantDescs.CD_long,
|
||||
Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("J_AsType", MethodTypeDesc.of(ConstantDescs.CD_long),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_long,
|
||||
Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("S", MethodTypeDesc.of(ConstantDescs.CD_short),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"S",
|
||||
ConstantDescs.CD_short,
|
||||
((int) Short.MAX_VALUE)))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("Z_F", MethodTypeDesc.of(ConstantDescs.CD_boolean),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"Z",
|
||||
ConstantDescs.CD_boolean,
|
||||
0))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("Z_T", MethodTypeDesc.of(ConstantDescs.CD_boolean),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"Z",
|
||||
ConstantDescs.CD_boolean,
|
||||
1))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("null", MethodTypeDesc.of(ConstantDescs.CD_Object),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"nullRef",
|
||||
ConstantDescs.CD_Object,
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
.withMethod("string", MethodTypeDesc.of(ConstantDescs.CD_String),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"string",
|
||||
ConstantDescs.CD_String,
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
.withMethod("stringArray", MethodTypeDesc.of(ConstantDescs.CD_String.arrayType()),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"stringArray",
|
||||
ConstantDescs.CD_String.arrayType(),
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
gc = MethodHandles.lookup().defineClass(byteArray);
|
||||
}
|
||||
|
@ -25,19 +25,21 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test for condy BSMs returning primitive values or null
|
||||
* @library /lib/testlibrary/bytecode
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyReturnPrimitiveTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyReturnPrimitiveTest
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
@ -96,124 +98,194 @@ public class CondyReturnPrimitiveTest {
|
||||
@BeforeClass
|
||||
public void generateClass() throws Exception {
|
||||
String genClassName = CondyReturnPrimitiveTest.class.getSimpleName() + "$Code";
|
||||
String bsmClassName = CondyReturnPrimitiveTest.class.getCanonicalName().replace('.', '/');
|
||||
String bsmClassDesc = CondyReturnPrimitiveTest.class.descriptorString();
|
||||
String bsmMethodName = "intConversion";
|
||||
String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class,
|
||||
String.class, Class.class, int.class).toMethodDescriptorString();
|
||||
|
||||
byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("B", "()B", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("B", "B", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Byte.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("C", "()C", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("C", "C", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Character.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("D", "()D", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("D", "D", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
))
|
||||
.withMethod("D_AsType", "()D", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "D", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
))
|
||||
.withMethod("F", "()F", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("F", "F", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
))
|
||||
.withMethod("F_AsType", "()F", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "F", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
))
|
||||
.withMethod("I", "()I", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "I", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("J", "()J", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("J", "J", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
))
|
||||
.withMethod("J_AsType", "()J", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "J", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
))
|
||||
.withMethod("S", "()S", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("S", "S", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Short.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("Z_F", "()Z", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(0))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("Z_T", "()Z", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(1))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("null", "()Ljava/lang/Object;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("nullRef", "Ljava/lang/Object;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.withMethod("string", "()Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("string", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.withMethod("stringArray", "()[Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("stringArray", "[Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.build();
|
||||
String.class, Class.class, int.class).toMethodDescriptorString();
|
||||
DirectMethodHandleDesc bsmMhDesc = MethodHandleDesc.of(
|
||||
DirectMethodHandleDesc.Kind.STATIC,
|
||||
ClassDesc.ofDescriptor(bsmClassDesc),
|
||||
bsmMethodName,
|
||||
bsmDescriptor
|
||||
);
|
||||
byte[] byteArray = Classfile.of().build(ClassDesc.of(genClassName), classBuilder -> classBuilder
|
||||
.withVersion(55, 0)
|
||||
.withSuperclass(ConstantDescs.CD_Object)
|
||||
.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, Classfile.ACC_PUBLIC,
|
||||
methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.aload(0)
|
||||
.invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME,
|
||||
ConstantDescs.MTD_void, false)
|
||||
.return_()
|
||||
)
|
||||
)
|
||||
.withMethod("B", MethodTypeDesc.of(ConstantDescs.CD_byte),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"B",
|
||||
ConstantDescs.CD_byte,
|
||||
(int) Byte.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("C", MethodTypeDesc.of(ConstantDescs.CD_char),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"C",
|
||||
ConstantDescs.CD_char,
|
||||
(int) Character.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("D", MethodTypeDesc.of(ConstantDescs.CD_double),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"D",
|
||||
ConstantDescs.CD_double,
|
||||
Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("D_AsType", MethodTypeDesc.of(ConstantDescs.CD_double),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_double,
|
||||
Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("F", MethodTypeDesc.of(ConstantDescs.CD_float),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"F",
|
||||
ConstantDescs.CD_float,
|
||||
Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
)
|
||||
)
|
||||
.withMethod("F_AsType", MethodTypeDesc.of(ConstantDescs.CD_float),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_float,
|
||||
Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
)
|
||||
)
|
||||
.withMethod("I", MethodTypeDesc.of(ConstantDescs.CD_int),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_int,
|
||||
Integer.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("J", MethodTypeDesc.of(ConstantDescs.CD_long),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"J",
|
||||
ConstantDescs.CD_long,
|
||||
Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("J_AsType", MethodTypeDesc.of(ConstantDescs.CD_long),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_long,
|
||||
Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("S", MethodTypeDesc.of(ConstantDescs.CD_short),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"S",
|
||||
ConstantDescs.CD_short,
|
||||
((int) Short.MAX_VALUE)))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("Z_F", MethodTypeDesc.of(ConstantDescs.CD_boolean),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"Z",
|
||||
ConstantDescs.CD_boolean,
|
||||
0))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("Z_T", MethodTypeDesc.of(ConstantDescs.CD_boolean),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"Z",
|
||||
ConstantDescs.CD_boolean,
|
||||
1))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("null", MethodTypeDesc.of(ConstantDescs.CD_Object),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"nullRef",
|
||||
ConstantDescs.CD_Object,
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
.withMethod("string", MethodTypeDesc.of(ConstantDescs.CD_String),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"string",
|
||||
ConstantDescs.CD_String,
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
.withMethod("stringArray", MethodTypeDesc.of(ConstantDescs.CD_String.arrayType()),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"stringArray",
|
||||
ConstantDescs.CD_String.arrayType(),
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
gc = MethodHandles.lookup().defineClass(byteArray);
|
||||
}
|
||||
|
@ -25,22 +25,23 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test bootstrap arguments for condy
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyStaticArgumentsTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyStaticArgumentsTest
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.PoolHelper;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.ConstantCallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandleInfo;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
@ -51,6 +52,8 @@ import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class CondyStaticArgumentsTest {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
private static final DirectMethodHandleDesc bigDecimalMhDesc = directMhDesc("bigDecimal");
|
||||
private static final DirectMethodHandleDesc mathContextMhDesc = directMhDesc("mathContext");
|
||||
|
||||
static class BSMInfo {
|
||||
final String methodName;
|
||||
@ -65,8 +68,7 @@ public class CondyStaticArgumentsTest {
|
||||
.get();
|
||||
try {
|
||||
handle = MethodHandles.lookup().unreflect(m);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
descriptor = handle.type().toMethodDescriptorString();
|
||||
@ -103,18 +105,18 @@ public class CondyStaticArgumentsTest {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "constant-name", String.class,
|
||||
bi.methodName, bi.handle.type(),
|
||||
S -> S.add(1).add(2L).add(3.0f).add(4.0d)
|
||||
.add("java/lang/Number", PoolHelper::putClass)
|
||||
.add("something", PoolHelper::putString)
|
||||
.add("(IJFD)V", PoolHelper::putMethodType)
|
||||
.add(mhi, (P, Z) -> {
|
||||
return P.putHandle(mhi.getReferenceKind(), "CondyStaticArgumentsTest", mhi.getName(), bi.descriptor);
|
||||
}));
|
||||
1, 2L, 3.0f, 4.0d,
|
||||
ClassDesc.ofDescriptor(Number.class.descriptorString()),
|
||||
"something",
|
||||
MethodTypeDesc.ofDescriptor("(IJFD)V"),
|
||||
MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.valueOf(mhi.getReferenceKind()),
|
||||
ClassDesc.ofDescriptor(mhi.getDeclaringClass().descriptorString()),
|
||||
mhi.getName(), MethodTypeDesc.ofDescriptor(mhi.getMethodType().descriptorString()))
|
||||
);
|
||||
|
||||
Assert.assertEquals(mh.invoke(), "constant-name-String-1-2-3.0-4.0-Number-something-(int,long,float,double)void-11");
|
||||
}
|
||||
|
||||
|
||||
static MathContext mathContext(MethodHandles.Lookup l, String value, Class<?> type) {
|
||||
switch (value) {
|
||||
case "UNLIMITED":
|
||||
@ -145,22 +147,6 @@ public class CondyStaticArgumentsTest {
|
||||
.toString();
|
||||
}
|
||||
|
||||
static <E> int bigDecimalPoolHelper(String value, String mc, PoolHelper<String, String, E> P) {
|
||||
BSMInfo bi = BSMInfo.of("bigDecimal");
|
||||
return P.putDynamicConstant("big-decimal", "Ljava/math/BigDecimal;", InstructionHelper.csym(L.lookupClass()), bi.methodName, bi.descriptor,
|
||||
S -> S.add(value, PoolHelper::putString)
|
||||
.add(mc, (P2, s) -> {
|
||||
return mathContextPoolHelper(s, P2);
|
||||
}));
|
||||
}
|
||||
|
||||
static <E> int mathContextPoolHelper(String mc, PoolHelper<String, String, E> P) {
|
||||
BSMInfo bi = BSMInfo.of("mathContext");
|
||||
return P.putDynamicConstant(mc, "Ljava/math/MathContext;", InstructionHelper.csym(L.lookupClass()), bi.methodName, bi.descriptor,
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCondyWithCondy() throws Throwable {
|
||||
BSMInfo bi = BSMInfo.of("condyWithCondy");
|
||||
@ -168,9 +154,18 @@ public class CondyStaticArgumentsTest {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "big-decimal-math-context", String.class,
|
||||
bi.methodName, bi.handle.type(),
|
||||
S -> S.add(null, (P, v) -> {
|
||||
return bigDecimalPoolHelper("3.14159265358979323846", "DECIMAL32", P);
|
||||
}));
|
||||
DynamicConstantDesc.ofNamed(
|
||||
bigDecimalMhDesc,
|
||||
"big-decimal",
|
||||
InstructionHelper.classDesc(BigDecimal.class),
|
||||
"3.14159265358979323846",
|
||||
DynamicConstantDesc.ofNamed(
|
||||
mathContextMhDesc,
|
||||
"DECIMAL32",
|
||||
InstructionHelper.classDesc(MathContext.class)
|
||||
)
|
||||
)
|
||||
);
|
||||
Assert.assertEquals(mh.invoke(), "big-decimal-math-context-String-3.141593-7");
|
||||
}
|
||||
|
||||
@ -193,9 +188,27 @@ public class CondyStaticArgumentsTest {
|
||||
MethodHandle mh = InstructionHelper.invokedynamic(
|
||||
L, "big-decimal-math-context", methodType(String.class),
|
||||
bi.methodName, bi.handle.type(),
|
||||
S -> S.add(null, (P, v) -> {
|
||||
return bigDecimalPoolHelper("3.14159265358979323846", "DECIMAL32", P);
|
||||
}));
|
||||
DynamicConstantDesc.ofNamed(
|
||||
bigDecimalMhDesc,
|
||||
"big-decimal",
|
||||
InstructionHelper.classDesc(BigDecimal.class),
|
||||
"3.14159265358979323846",
|
||||
DynamicConstantDesc.ofNamed(
|
||||
mathContextMhDesc,
|
||||
"DECIMAL32",
|
||||
InstructionHelper.classDesc(MathContext.class)
|
||||
)
|
||||
));
|
||||
Assert.assertEquals(mh.invoke(), "big-decimal-math-context-()Ljava/lang/String;-3.141593-7");
|
||||
}
|
||||
|
||||
private static DirectMethodHandleDesc directMhDesc(String methodName) {
|
||||
MethodHandleInfo mhi = MethodHandles.lookup().revealDirect(BSMInfo.of(methodName).handle);
|
||||
return MethodHandleDesc.of(
|
||||
DirectMethodHandleDesc.Kind.valueOf(mhi.getReferenceKind()),
|
||||
ClassDesc.ofDescriptor(mhi.getDeclaringClass().descriptorString()),
|
||||
mhi.getName(),
|
||||
mhi.getMethodType().descriptorString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,17 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test invalid name in name and type
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyTypeValidationTest
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
@ -49,29 +55,36 @@ public class CondyTypeValidationTest {
|
||||
.toMethodDescriptorString();
|
||||
|
||||
@DataProvider
|
||||
public Object[][] invalidTypesProvider() throws Exception {
|
||||
public Object[][] invalidTypesProvider() {
|
||||
return Stream.of(
|
||||
// ByteCode API checks for the following invalid types
|
||||
// "",
|
||||
// "[",
|
||||
// "A",
|
||||
// "a",
|
||||
"L/java/lang/Object",
|
||||
Stream.generate(() -> "[").limit(256).collect(Collectors.joining("", "", "I")))
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
new Object[]{"L/java/lang/Object", "not a valid reference type descriptor"},
|
||||
new Object[]{
|
||||
Stream.generate(() -> "[").limit(256)
|
||||
.collect(Collectors.joining("", "", "I")),
|
||||
"Cannot create an array type descriptor with more than 255 dimensions"
|
||||
}
|
||||
).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "invalidTypesProvider", expectedExceptions = ClassFormatError.class)
|
||||
public void testInvalidTypes(String type) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", type,
|
||||
"bsm", BSM_TYPE,
|
||||
S -> {
|
||||
});
|
||||
@Test(dataProvider = "invalidTypesProvider")
|
||||
public void testInvalidTypes(String type, String errMessContent) throws Exception {
|
||||
try {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", type,
|
||||
"bsm", BSM_TYPE
|
||||
);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertTrue(e.getMessage().contains(errMessContent));
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public Object[][] validTypesProvider() throws Exception {
|
||||
public Object[][] validTypesProvider() {
|
||||
List<String> t = new ArrayList<>(List.of("B", "C", "D", "F", "I", "J", "Ljava/lang/Object;", "S", "Z"));
|
||||
int l = t.size();
|
||||
for (int i = 0; i < l; i++) {
|
||||
@ -86,8 +99,7 @@ public class CondyTypeValidationTest {
|
||||
public void testValidTypes(String type) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", type,
|
||||
"bsm", BSM_TYPE,
|
||||
S -> {
|
||||
});
|
||||
"bsm", BSM_TYPE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,25 +25,29 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Stress test ldc to ensure HotSpot correctly manages oop maps
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyWithGarbageTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWithGarbageTest
|
||||
*/
|
||||
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.CodeBuilder;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static test.java.lang.invoke.lib.InstructionHelper.cref;
|
||||
import static test.java.lang.invoke.lib.InstructionHelper.csym;
|
||||
import static test.java.lang.invoke.lib.InstructionHelper.classDesc;
|
||||
|
||||
public class CondyWithGarbageTest {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
@ -65,46 +69,65 @@ public class CondyWithGarbageTest {
|
||||
}
|
||||
|
||||
static MethodHandle lcdStringBasher() throws Exception {
|
||||
byte[] byteArray = new BasicClassBuilder(csym(L.lookupClass()) + "$Code$String", 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("m", "()" + cref(String.class), M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C -> {
|
||||
C.new_(csym(StringBuilder.class))
|
||||
.dup()
|
||||
.invokespecial(csym(StringBuilder.class), "<init>", "()V", false)
|
||||
.astore_0();
|
||||
ClassDesc cd = classDesc(L.lookupClass(), "$Code$String");
|
||||
byte[] bytes = Classfile.of().build(cd, classBuilder -> classBuilder
|
||||
.withVersion(55, 0)
|
||||
.withSuperclass(ConstantDescs.CD_Object)
|
||||
.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, Classfile.ACC_PUBLIC,
|
||||
methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.aload(0)
|
||||
.invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME,
|
||||
ConstantDescs.MTD_void, false)
|
||||
.return_()))
|
||||
.withMethod("m", MethodTypeDesc.of(ConstantDescs.CD_String),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> {
|
||||
codeBuilder
|
||||
.new_(classDesc(StringBuilder.class))
|
||||
.dup()
|
||||
.invokespecial(classDesc(StringBuilder.class), ConstantDescs.INIT_NAME,
|
||||
ConstantDescs.MTD_void, false)
|
||||
.astore(0);
|
||||
|
||||
for (int i = 10; i < 100; i++) {
|
||||
ldcString(C, Integer.toString(i));
|
||||
C.astore_1().aload_0().aload_1();
|
||||
C.invokevirtual(csym(StringBuilder.class), "append", methodType(StringBuilder.class, String.class).toMethodDescriptorString(), false);
|
||||
C.pop();
|
||||
}
|
||||
for (int i = 10; i < 100; i++) {
|
||||
ldcString(codeBuilder, Integer.toString(i));
|
||||
codeBuilder
|
||||
.astore(1)
|
||||
.aload(0)
|
||||
.aload(1)
|
||||
.invokevirtual(
|
||||
classDesc(StringBuilder.class),
|
||||
"append",
|
||||
MethodTypeDesc.of(
|
||||
classDesc(StringBuilder.class),
|
||||
classDesc(String.class)))
|
||||
.pop();
|
||||
}
|
||||
|
||||
C.aload_0();
|
||||
C.invokevirtual(csym(StringBuilder.class), "toString", methodType(String.class).toMethodDescriptorString(), false);
|
||||
C.areturn();
|
||||
}
|
||||
))
|
||||
.build();
|
||||
|
||||
Class<?> gc = L.defineClass(byteArray);
|
||||
codeBuilder
|
||||
.aload(0)
|
||||
.invokevirtual(
|
||||
classDesc(StringBuilder.class),
|
||||
"toString",
|
||||
MethodTypeDesc.of(classDesc(String.class)))
|
||||
.areturn();
|
||||
}
|
||||
)));
|
||||
Class<?> gc = L.defineClass(bytes);
|
||||
return L.findStatic(gc, "m", methodType(String.class));
|
||||
}
|
||||
|
||||
static void ldcString(TypedCodeBuilder<String, String, byte[], ?> C, String name) {
|
||||
C.ldc(name, cref(String.class),
|
||||
csym(L.lookupClass()),
|
||||
"bsmString",
|
||||
methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
|
||||
S -> {
|
||||
});
|
||||
private static void ldcString(CodeBuilder codeBuilder, String name) {
|
||||
codeBuilder.ldc(DynamicConstantDesc.ofNamed(
|
||||
MethodHandleDesc.of(
|
||||
DirectMethodHandleDesc.Kind.STATIC,
|
||||
classDesc(L.lookupClass()),
|
||||
"bsmString",
|
||||
methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString()),
|
||||
name,
|
||||
classDesc(String.class)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -125,46 +148,67 @@ public class CondyWithGarbageTest {
|
||||
}
|
||||
|
||||
static MethodHandle lcdStringArrayBasher() throws Exception {
|
||||
byte[] byteArray = new BasicClassBuilder(csym(L.lookupClass()) + "$Code$StringArray", 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("m", "()" + cref(String.class), M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C -> {
|
||||
C.new_(csym(StringBuilder.class))
|
||||
.dup()
|
||||
.invokespecial(csym(StringBuilder.class), "<init>", "()V", false)
|
||||
.astore_0();
|
||||
ClassDesc cd = classDesc(L.lookupClass(), "$Code$StringArray");
|
||||
byte[] bytes = Classfile.of().build(cd, classBuilder -> classBuilder
|
||||
.withVersion(55, 0)
|
||||
.withSuperclass(ConstantDescs.CD_Object)
|
||||
.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, Classfile.ACC_PUBLIC,
|
||||
methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.aload(0)
|
||||
.invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME,
|
||||
ConstantDescs.MTD_void, false)
|
||||
.return_()))
|
||||
.withMethod("m", MethodTypeDesc.of(classDesc(String.class)),
|
||||
Classfile.ACC_PUBLIC + Classfile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> {
|
||||
codeBuilder
|
||||
.new_(classDesc(StringBuilder.class))
|
||||
.dup()
|
||||
.invokespecial(classDesc(StringBuilder.class),
|
||||
ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, false)
|
||||
.astore(0);
|
||||
|
||||
for (int i = 10; i < 100; i++) {
|
||||
ldcStringArray(C, Integer.toString(i));
|
||||
C.bipush(0).aaload().astore_1();
|
||||
C.aload_0().aload_1();
|
||||
C.invokevirtual(csym(StringBuilder.class), "append", methodType(StringBuilder.class, String.class).toMethodDescriptorString(), false);
|
||||
C.pop();
|
||||
}
|
||||
for (int i = 10; i < 100; i++) {
|
||||
ldcStringArray(codeBuilder, Integer.toString(i));
|
||||
codeBuilder
|
||||
.bipush(0)
|
||||
.aaload()
|
||||
.astore(1)
|
||||
.aload(0)
|
||||
.aload(1)
|
||||
.invokevirtual(
|
||||
classDesc(StringBuilder.class),
|
||||
"append",
|
||||
MethodTypeDesc.of(
|
||||
classDesc(StringBuilder.class),
|
||||
classDesc(String.class)))
|
||||
.pop();
|
||||
}
|
||||
|
||||
C.aload_0();
|
||||
C.invokevirtual(csym(StringBuilder.class), "toString", methodType(String.class).toMethodDescriptorString(), false);
|
||||
C.areturn();
|
||||
}
|
||||
))
|
||||
.build();
|
||||
|
||||
Class<?> gc = L.defineClass(byteArray);
|
||||
codeBuilder
|
||||
.aload(0)
|
||||
.invokevirtual(
|
||||
classDesc(StringBuilder.class),
|
||||
"toString",
|
||||
MethodTypeDesc.of(classDesc(String.class)))
|
||||
.areturn();
|
||||
}
|
||||
)));
|
||||
Class<?> gc = L.defineClass(bytes);
|
||||
return L.findStatic(gc, "m", methodType(String.class));
|
||||
}
|
||||
|
||||
static void ldcStringArray(TypedCodeBuilder<String, String, byte[], ?> C, String name) {
|
||||
C.ldc(name, cref(String[].class),
|
||||
csym(L.lookupClass()),
|
||||
"bsmStringArray",
|
||||
methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
|
||||
S -> {
|
||||
});
|
||||
static void ldcStringArray(CodeBuilder codeBuilder, String name) {
|
||||
codeBuilder.ldc(DynamicConstantDesc.ofNamed(
|
||||
MethodHandleDesc.of(
|
||||
DirectMethodHandleDesc.Kind.STATIC,
|
||||
classDesc(L.lookupClass()),
|
||||
"bsmStringArray",
|
||||
methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString()
|
||||
),
|
||||
name,
|
||||
classDesc(String[].class)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,13 @@
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test bootstrap methods returning the wrong type
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng CondyWrongType
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWrongType
|
||||
*/
|
||||
@ -60,7 +65,7 @@ public class CondyWrongType {
|
||||
"J", long.class,
|
||||
"S", short.class,
|
||||
"Z", boolean.class
|
||||
);
|
||||
);
|
||||
|
||||
List<Object[]> cases = new ArrayList<>();
|
||||
for (String name : typeMap.keySet()) {
|
||||
@ -71,11 +76,10 @@ public class CondyWrongType {
|
||||
boolean pass = true;
|
||||
try {
|
||||
zero.asType(MethodType.methodType(typeMap.get(type)));
|
||||
}
|
||||
catch (WrongMethodTypeException e) {
|
||||
} catch (WrongMethodTypeException e) {
|
||||
pass = false;
|
||||
}
|
||||
cases.add(new Object[] { name, type, pass});
|
||||
cases.add(new Object[]{name, type, pass});
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,20 +114,17 @@ public class CondyWrongType {
|
||||
Throwable caught = null;
|
||||
try {
|
||||
mh.invoke();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
} catch (Throwable t) {
|
||||
caught = t;
|
||||
}
|
||||
|
||||
if (caught == null) {
|
||||
if (pass) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Assert.fail("Throwable expected");
|
||||
}
|
||||
}
|
||||
else if (pass) {
|
||||
} else if (pass) {
|
||||
Assert.fail("Throwable not expected");
|
||||
}
|
||||
|
||||
@ -165,8 +166,8 @@ public class CondyWrongType {
|
||||
return InstructionHelper.ldcDynamicConstant(
|
||||
MethodHandles.lookup(),
|
||||
name, type,
|
||||
"bsm", methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
|
||||
S -> { });
|
||||
"bsm",
|
||||
methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).descriptorString());
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
@ -25,23 +25,24 @@
|
||||
* @test
|
||||
* @bug 8186046 8195694
|
||||
* @summary Test dynamic constant bootstraps
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @library /java/lang/invoke/common
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
* @modules java.base/jdk.internal.classfile
|
||||
* java.base/jdk.internal.classfile.attribute
|
||||
* java.base/jdk.internal.classfile.constantpool
|
||||
* java.base/jdk.internal.classfile.instruction
|
||||
* java.base/jdk.internal.classfile.components
|
||||
* @run testng ConstantBootstrapsTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 ConstantBootstrapsTest
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.PoolHelper;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.ConstantBootstraps;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandleInfo;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DirectMethodHandleDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.lang.invoke.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -61,13 +62,11 @@ public class ConstantBootstrapsTest {
|
||||
|
||||
public void testNullConstant() throws Throwable {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(L, "_", Object.class,
|
||||
ConstantBootstraps.class, "nullConstant", lookupMT(Object.class),
|
||||
S -> {});
|
||||
ConstantBootstraps.class, "nullConstant", lookupMT(Object.class));
|
||||
assertNull(handle.invoke());
|
||||
|
||||
handle = InstructionHelper.ldcDynamicConstant(L, "_", MethodType.class,
|
||||
ConstantBootstraps.class, "nullConstant", lookupMT(Object.class),
|
||||
S -> {});
|
||||
ConstantBootstraps.class, "nullConstant", lookupMT(Object.class));
|
||||
assertNull(handle.invoke());
|
||||
}
|
||||
|
||||
@ -92,8 +91,7 @@ public class ConstantBootstrapsTest {
|
||||
|
||||
for (var desc : pm.keySet()) {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(L, desc, Class.class,
|
||||
ConstantBootstraps.class, "primitiveClass", lookupMT(Class.class),
|
||||
S -> {});
|
||||
ConstantBootstraps.class, "primitiveClass", lookupMT(Class.class));
|
||||
assertEquals(handle.invoke(), pm.get(desc));
|
||||
}
|
||||
}
|
||||
@ -127,8 +125,7 @@ public class ConstantBootstrapsTest {
|
||||
public void testEnumConstant() throws Throwable {
|
||||
for (var v : StackWalker.Option.values()) {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(L, v.name(), StackWalker.Option.class,
|
||||
ConstantBootstraps.class, "enumConstant", lookupMT(Enum.class),
|
||||
S -> { });
|
||||
ConstantBootstraps.class, "enumConstant", lookupMT(Enum.class));
|
||||
assertEquals(handle.invoke(), v);
|
||||
}
|
||||
}
|
||||
@ -141,21 +138,19 @@ public class ConstantBootstrapsTest {
|
||||
|
||||
public void testGetStaticDecl() throws Throwable {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(L, "TYPE", Class.class,
|
||||
ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class, Class.class),
|
||||
S -> { S.add("java/lang/Integer", PoolHelper::putClass); });
|
||||
ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class, Class.class),
|
||||
InstructionHelper.classDesc(Integer.class));
|
||||
assertEquals(handle.invoke(), int.class);
|
||||
}
|
||||
|
||||
public void testGetStaticSelf() throws Throwable {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(L, "MAX_VALUE", int.class,
|
||||
ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class),
|
||||
S -> { });
|
||||
ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class));
|
||||
assertEquals(handle.invoke(), Integer.MAX_VALUE);
|
||||
|
||||
|
||||
handle = InstructionHelper.ldcDynamicConstant(L, "ZERO", BigInteger.class,
|
||||
ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class),
|
||||
S -> { });
|
||||
ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class));
|
||||
assertEquals(handle.invoke(), BigInteger.ZERO);
|
||||
}
|
||||
|
||||
@ -164,14 +159,10 @@ public class ConstantBootstrapsTest {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(
|
||||
L, "_", List.class,
|
||||
ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
|
||||
S -> {
|
||||
S.add("", (P, Z) -> {
|
||||
return P.putHandle(MethodHandleInfo.REF_invokeStatic, "java/util/List", "of",
|
||||
MethodType.methodType(List.class, Object[].class).toMethodDescriptorString(),
|
||||
true);
|
||||
});
|
||||
S.add(1).add(2).add(3).add(4);
|
||||
});
|
||||
MethodHandleDesc.of(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, ConstantDescs.CD_List, "of",
|
||||
MethodType.methodType(List.class, Object[].class).toMethodDescriptorString()),
|
||||
1, 2, 3, 4
|
||||
);
|
||||
assertEquals(handle.invoke(), List.of(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
@ -179,14 +170,10 @@ public class ConstantBootstrapsTest {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(
|
||||
L, "_", int.class,
|
||||
ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
|
||||
S -> {
|
||||
S.add("", (P, Z) -> {
|
||||
return P.putHandle(MethodHandleInfo.REF_invokeStatic, "java/lang/Integer", "valueOf",
|
||||
MethodType.methodType(Integer.class, String.class).toMethodDescriptorString(),
|
||||
false);
|
||||
});
|
||||
S.add("42");
|
||||
});
|
||||
MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, ConstantDescs.CD_Integer, "valueOf",
|
||||
MethodType.methodType(Integer.class, String.class).toMethodDescriptorString()),
|
||||
"42"
|
||||
);
|
||||
assertEquals(handle.invoke(), 42);
|
||||
}
|
||||
|
||||
@ -195,29 +182,25 @@ public class ConstantBootstrapsTest {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(
|
||||
L, "_", Collection.class,
|
||||
ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
|
||||
S -> {
|
||||
S.add("", (P, Z) -> {
|
||||
return P.putHandle(MethodHandleInfo.REF_invokeStatic, "java/util/List", "of",
|
||||
MethodType.methodType(List.class, Object[].class).toMethodDescriptorString(),
|
||||
true);
|
||||
});
|
||||
S.add(1).add(2).add(3).add(4);
|
||||
});
|
||||
MethodHandleDesc.of(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, ConstantDescs.CD_List, "of",
|
||||
MethodType.methodType(List.class, Object[].class).toMethodDescriptorString()),
|
||||
1, 2, 3, 4
|
||||
);
|
||||
assertEquals(handle.invoke(), List.of(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ClassCastException.class)
|
||||
public void testInvokeAsTypeClassCast() throws Throwable {
|
||||
ConstantBootstraps.invoke(MethodHandles.lookup(), "_", String.class,
|
||||
MethodHandles.lookup().findStatic(Integer.class, "valueOf", MethodType.methodType(Integer.class, String.class)),
|
||||
"42");
|
||||
MethodHandles.lookup().findStatic(Integer.class, "valueOf", MethodType.methodType(Integer.class, String.class)),
|
||||
"42");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = WrongMethodTypeException.class)
|
||||
public void testInvokeAsTypeWrongReturnType() throws Throwable {
|
||||
ConstantBootstraps.invoke(MethodHandles.lookup(), "_", short.class,
|
||||
MethodHandles.lookup().findStatic(Integer.class, "parseInt", MethodType.methodType(int.class, String.class)),
|
||||
"42");
|
||||
MethodHandles.lookup().findStatic(Integer.class, "parseInt", MethodType.methodType(int.class, String.class)),
|
||||
"42");
|
||||
}
|
||||
|
||||
|
||||
@ -230,10 +213,9 @@ public class ConstantBootstrapsTest {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(
|
||||
L, "f", VarHandle.class,
|
||||
ConstantBootstraps.class, "fieldVarHandle", lookupMT(VarHandle.class, Class.class, Class.class),
|
||||
S -> {
|
||||
S.add(X.class.getName().replace('.', '/'), PoolHelper::putClass).
|
||||
add("java/lang/String", PoolHelper::putClass);
|
||||
});
|
||||
InstructionHelper.classDesc(X.class),
|
||||
InstructionHelper.classDesc(String.class)
|
||||
);
|
||||
|
||||
var vhandle = (VarHandle) handle.invoke();
|
||||
assertEquals(vhandle.varType(), String.class);
|
||||
@ -244,10 +226,9 @@ public class ConstantBootstrapsTest {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(
|
||||
L, "sf", VarHandle.class,
|
||||
ConstantBootstraps.class, "staticFieldVarHandle", lookupMT(VarHandle.class, Class.class, Class.class),
|
||||
S -> {
|
||||
S.add(X.class.getName().replace('.', '/'), PoolHelper::putClass).
|
||||
add("java/lang/String", PoolHelper::putClass);
|
||||
});
|
||||
InstructionHelper.classDesc(X.class),
|
||||
InstructionHelper.classDesc(String.class)
|
||||
);
|
||||
|
||||
var vhandle = (VarHandle) handle.invoke();
|
||||
assertEquals(vhandle.varType(), String.class);
|
||||
@ -258,9 +239,8 @@ public class ConstantBootstrapsTest {
|
||||
var handle = InstructionHelper.ldcDynamicConstant(
|
||||
L, "_", VarHandle.class,
|
||||
ConstantBootstraps.class, "arrayVarHandle", lookupMT(VarHandle.class, Class.class),
|
||||
S -> {
|
||||
S.add(String[].class.getName().replace('.', '/'), PoolHelper::putClass);
|
||||
});
|
||||
InstructionHelper.classDesc(String[].class)
|
||||
);
|
||||
|
||||
var vhandle = (VarHandle) handle.invoke();
|
||||
assertEquals(vhandle.varType(), String.class);
|
||||
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Base builder.
|
||||
*
|
||||
* @param <S> the type of the symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <D> the type of this builder
|
||||
*/
|
||||
public class AbstractBuilder<S, T, E, D extends AbstractBuilder<S, T, E, D>> {
|
||||
/**
|
||||
* The helper to build the constant pool.
|
||||
*/
|
||||
protected final PoolHelper<S, T, E> poolHelper;
|
||||
|
||||
/**
|
||||
* The helper to use to manipulate type descriptors.
|
||||
*/
|
||||
protected final TypeHelper<S, T> typeHelper;
|
||||
|
||||
/**
|
||||
* Create a builder.
|
||||
*
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
AbstractBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
this.poolHelper = poolHelper;
|
||||
this.typeHelper = typeHelper;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
D thisBuilder() {
|
||||
return (D) this;
|
||||
}
|
||||
}
|
@ -1,338 +0,0 @@
|
||||
/*
|
||||
* 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.util.function.Consumer;
|
||||
import java.util.function.ToIntBiFunction;
|
||||
|
||||
public class AnnotationsBuilder<S, T, E> extends AbstractBuilder<S, T, E, AnnotationsBuilder<S, T, E>> {
|
||||
|
||||
GrowableByteBuffer annoAttribute;
|
||||
int nannos;
|
||||
|
||||
AnnotationsBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
this.annoAttribute = new GrowableByteBuffer();
|
||||
annoAttribute.writeChar(0);
|
||||
}
|
||||
|
||||
public enum Kind {
|
||||
RUNTIME_VISIBLE,
|
||||
RUNTIME_INVISIBLE;
|
||||
}
|
||||
|
||||
enum Tag {
|
||||
B('B'),
|
||||
C('C'),
|
||||
D('D'),
|
||||
F('F'),
|
||||
I('I'),
|
||||
J('J'),
|
||||
S('S'),
|
||||
Z('Z'),
|
||||
STRING('s'),
|
||||
ENUM('e'),
|
||||
CLASS('c'),
|
||||
ANNO('@'),
|
||||
ARRAY('[');
|
||||
|
||||
char tagChar;
|
||||
|
||||
Tag(char tagChar) {
|
||||
this.tagChar = tagChar;
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationsBuilder<S, T, E> withAnnotation(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
annoAttribute.writeChar(poolHelper.putType(annoType));
|
||||
int offset = annoAttribute.offset;
|
||||
annoAttribute.writeChar(0);
|
||||
if (annotationBuilder != null) {
|
||||
AnnotationElementBuilder _builder = new AnnotationElementBuilder();
|
||||
int nelems = _builder.withElements(annotationBuilder);
|
||||
patchCharAt(offset, nelems);
|
||||
}
|
||||
nannos++;
|
||||
return this;
|
||||
}
|
||||
|
||||
byte[] build() {
|
||||
patchCharAt(0, nannos);
|
||||
return annoAttribute.bytes();
|
||||
}
|
||||
|
||||
private void patchCharAt(int offset, int newChar) {
|
||||
int prevOffset = annoAttribute.offset;
|
||||
try {
|
||||
annoAttribute.offset = offset;
|
||||
annoAttribute.writeChar(newChar);
|
||||
} finally {
|
||||
annoAttribute.offset = prevOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
static Consumer NO_BUILDER =
|
||||
new Consumer() {
|
||||
@Override
|
||||
public void accept(Object o) {
|
||||
//do nothing
|
||||
}
|
||||
};
|
||||
|
||||
public class AnnotationElementBuilder {
|
||||
|
||||
int nelems;
|
||||
|
||||
public AnnotationElementBuilder withString(String name, String s) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writeStringValue(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void writeStringValue(String s) {
|
||||
annoAttribute.writeByte(Tag.STRING.tagChar);
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(s));
|
||||
nelems++;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withClass(String name, T s) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writeClassValue(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void writeClassValue(T s) {
|
||||
annoAttribute.writeByte(Tag.CLASS.tagChar);
|
||||
annoAttribute.writeChar(poolHelper.putType(s));
|
||||
nelems++;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withEnum(String name, T enumType, int constant) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writeEnumValue(enumType, constant);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void writeEnumValue(T enumType, int constant) {
|
||||
annoAttribute.writeByte(Tag.ENUM.tagChar);
|
||||
annoAttribute.writeChar(poolHelper.putType(enumType));
|
||||
annoAttribute.writeChar(constant);
|
||||
nelems++;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withAnnotation(String name, T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writeAnnotationValue(annoType, annotationBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void writeAnnotationValue(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
annoAttribute.writeByte(Tag.ANNO.tagChar);
|
||||
annoAttribute.writeChar(poolHelper.putType(annoType));
|
||||
int offset = annoAttribute.offset;
|
||||
annoAttribute.writeChar(0);
|
||||
int nelems = withNestedElements(annotationBuilder);
|
||||
patchCharAt(offset, nelems);
|
||||
this.nelems++;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, char c) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, short s) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, byte b) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, int i) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.I, i, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, float f) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, long l) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, double d) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, boolean b) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
private <Z> void writePrimitiveValue(Tag tag, Z value, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc) {
|
||||
annoAttribute.writeByte(tag.tagChar);
|
||||
annoAttribute.writeChar(poolFunc.applyAsInt(poolHelper, value));
|
||||
nelems++;
|
||||
}
|
||||
|
||||
AnnotationElementBuilder withStrings(String name, String... ss) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ss.length);
|
||||
for (String s : ss) {
|
||||
writeStringValue(s);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
AnnotationElementBuilder withClasses(String name, T... cc) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(cc.length);
|
||||
for (T c : cc) {
|
||||
writeClassValue(c);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
AnnotationElementBuilder withEnums(String name, T enumType, int... constants) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(constants.length);
|
||||
for (int c : constants) {
|
||||
writeEnumValue(enumType, c);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public AnnotationElementBuilder withAnnotations(String name, T annoType, Consumer<? super AnnotationElementBuilder>... annotationBuilders) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(annotationBuilders.length);
|
||||
for (Consumer<? super AnnotationElementBuilder> annotationBuilder : annotationBuilders) {
|
||||
writeAnnotationValue(annoType, annotationBuilder);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, char... cc) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(cc.length);
|
||||
for (char c : cc) {
|
||||
writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, short... ss) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ss.length);
|
||||
for (short s : ss) {
|
||||
writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, byte... bb) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(bb.length);
|
||||
for (byte b : bb) {
|
||||
writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, int... ii) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ii.length);
|
||||
for (int i : ii) {
|
||||
writePrimitiveValue(Tag.I, i, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, float... ff) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ff.length);
|
||||
for (float f : ff) {
|
||||
writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, long... ll) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ll.length);
|
||||
for (long l : ll) {
|
||||
writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, double... dd) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(dd.length);
|
||||
for (double d : dd) {
|
||||
writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, boolean... bb) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(bb.length);
|
||||
for (boolean b : bb) {
|
||||
writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
int withNestedElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
return withElements(new AnnotationElementBuilder(), annotationBuilder);
|
||||
}
|
||||
|
||||
int withElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
return withElements(this, annotationBuilder);
|
||||
}
|
||||
|
||||
private int withElements(AnnotationElementBuilder builder, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
annotationBuilder.accept(builder);
|
||||
return builder.nelems;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Base builder for attribute containing class file entities.
|
||||
*
|
||||
* @param <S> the type of the symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <D> the type of this builder
|
||||
*/
|
||||
public class AttributeBuilder<S, T, E, D extends AttributeBuilder<S, T, E, D>>
|
||||
extends AbstractBuilder<S, T, E, D> {
|
||||
|
||||
/**
|
||||
* The number of attributes.
|
||||
*/
|
||||
protected int nattrs;
|
||||
|
||||
/**
|
||||
* The attributes represented as bytes.
|
||||
*/
|
||||
protected GrowableByteBuffer attributes = new GrowableByteBuffer();
|
||||
|
||||
/**
|
||||
* Create an attribute builder.
|
||||
*
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
public AttributeBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class file Attribute. Defined as:
|
||||
* <pre>
|
||||
* {@code attribute_info {
|
||||
* u2 attribute_name_index;
|
||||
* u4 attribute_length;
|
||||
* u1 info[attribute_length];
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name the attribute name
|
||||
* @param bytes the bytes of the attribute info
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public D withAttribute(CharSequence name, byte[] bytes) {
|
||||
attributes.writeChar(poolHelper.putUtf8(name));
|
||||
attributes.writeInt(bytes.length);
|
||||
attributes.writeBytes(bytes);
|
||||
nattrs++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class file Attribute, using a writer. Defined as:
|
||||
* <pre>
|
||||
* {@code attribute_info {
|
||||
* u2 attribute_name_index;
|
||||
* u4 attribute_length;
|
||||
* u1 info[attribute_length];
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param <Z> the type of the object representing the attribute
|
||||
* @param name the attribute name
|
||||
* @param attr the representation of the attribute
|
||||
* @param attrWriter the writer which transform the attribute representation into bytes
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public <Z> D withAttribute(CharSequence name, Z attr, AttributeWriter<S, T, E, Z> attrWriter) {
|
||||
attributes.writeChar(poolHelper.putUtf8(name));
|
||||
int offset = attributes.offset;
|
||||
attributes.writeInt(0);
|
||||
attrWriter.write(attr, poolHelper, attributes);
|
||||
int len = attributes.offset - offset - 4;
|
||||
attributes.withOffset(offset, buf -> buf.writeInt(len));
|
||||
nattrs++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writer for transforming attribute representations to bytes
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <A> the type of the object representing the attribute
|
||||
*/
|
||||
public interface AttributeWriter<S, T, E, A> {
|
||||
|
||||
/**
|
||||
* Write an attribute representation into a byte buffer.
|
||||
*
|
||||
* @param attr the representation of the attribute
|
||||
* @param poolHelper the constant pool helper
|
||||
* @param buf the buffer to collect the bytes
|
||||
*/
|
||||
void write(A attr, PoolHelper<S, T, E> poolHelper, GrowableByteBuffer buf);
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public class BasicClassBuilder extends ClassBuilder<String, String, BasicClassBuilder> {
|
||||
|
||||
public BasicClassBuilder(String thisClass, int majorVersion, int minorVersion) {
|
||||
this();
|
||||
withMinorVersion(minorVersion);
|
||||
withMajorVersion(majorVersion);
|
||||
withThisClass(thisClass);
|
||||
}
|
||||
|
||||
public BasicClassBuilder() {
|
||||
super(new BytePoolHelper<>(s->s, s->s), new BasicTypeHelper());
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
* 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.util.Iterator;
|
||||
|
||||
/**
|
||||
* Helper to create and manipulate type descriptors, where type descriptors
|
||||
* are represented as JVM type descriptor strings and symbols are represented
|
||||
* as name strings
|
||||
*/
|
||||
public class BasicTypeHelper implements TypeHelper<String, String> {
|
||||
|
||||
@Override
|
||||
public String elemtype(String s) {
|
||||
if (!s.startsWith("[")) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return s.substring(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String arrayOf(String s) {
|
||||
return "[" + s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type(String s) {
|
||||
return "L" + s + ";";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeTag tag(String s) {
|
||||
switch (s.charAt(0)) {
|
||||
case '[':
|
||||
case 'L':
|
||||
return TypeTag.A;
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'Z':
|
||||
case 'S':
|
||||
case 'I':
|
||||
return TypeTag.I;
|
||||
case 'F':
|
||||
return TypeTag.F;
|
||||
case 'J':
|
||||
return TypeTag.J;
|
||||
case 'D':
|
||||
return TypeTag.D;
|
||||
case 'V':
|
||||
return TypeTag.V;
|
||||
case 'Q':
|
||||
return TypeTag.Q;
|
||||
default:
|
||||
throw new IllegalStateException("Bad type: " + s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nullType() {
|
||||
// Needed in TypedCodeBuilder; ACONST_NULL pushes a 'null' onto the stack,
|
||||
// and stack maps handle null differently
|
||||
return "<null>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String commonSupertype(String t1, String t2) {
|
||||
if (t1.equals(t2)) {
|
||||
return t1;
|
||||
} else {
|
||||
try {
|
||||
Class<?> c1 = from(t1);
|
||||
Class<?> c2 = from(t2);
|
||||
if (c1.isAssignableFrom(c2)) {
|
||||
return t1;
|
||||
} else if (c2.isAssignableFrom(c1)) {
|
||||
return t2;
|
||||
} else {
|
||||
return "Ljava/lang/Object;";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Class<?> from(String desc) throws ReflectiveOperationException {
|
||||
if (desc.startsWith("[")) {
|
||||
return Class.forName(desc.replaceAll("/", "."));
|
||||
} else {
|
||||
return Class.forName(symbol(desc).replaceAll("/", "."));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> parameterTypes(String s) {
|
||||
//TODO: gracefully non-method types
|
||||
return new Iterator<String>() {
|
||||
int ch = 1;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return s.charAt(ch) != ')';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
char curr = s.charAt(ch);
|
||||
switch (curr) {
|
||||
case 'C':
|
||||
case 'B':
|
||||
case 'S':
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'F':
|
||||
case 'D':
|
||||
case 'Z':
|
||||
ch++;
|
||||
return String.valueOf(curr);
|
||||
case '[':
|
||||
ch++;
|
||||
return "[" + next();
|
||||
case 'L':
|
||||
case 'Q':
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (curr != ';') {
|
||||
builder.append(curr);
|
||||
curr = s.charAt(++ch);
|
||||
}
|
||||
builder.append(';');
|
||||
ch++;
|
||||
return builder.toString();
|
||||
default:
|
||||
throw new AssertionError("cannot parse string: " + s);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String symbolFrom(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromTag(TypeTag tag) {
|
||||
return tag.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String symbol(String type) {
|
||||
return (type.startsWith("L") || type.startsWith("Q")) ? type.substring(1, type.length() - 1) : type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String returnType(String s) {
|
||||
return s.substring(s.indexOf(')') + 1, s.length());
|
||||
}
|
||||
|
||||
}
|
@ -1,752 +0,0 @@
|
||||
/*
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,240 +0,0 @@
|
||||
/*
|
||||
* 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.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Base class builder. The base of higher level class builders.
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <C> the type of this builder
|
||||
*/
|
||||
public class ClassBuilder<S, T, C extends ClassBuilder<S, T, C>>
|
||||
extends DeclBuilder<S, T, byte[], C> {
|
||||
|
||||
/**
|
||||
* The helper to use to manipulate type descriptors.
|
||||
*/
|
||||
protected TypeHelper<S, T> typeHelper;
|
||||
|
||||
/**
|
||||
* The symbol for the class being built.
|
||||
*/
|
||||
protected S thisClass;
|
||||
|
||||
/**
|
||||
* The super-interfaces of the class being built..
|
||||
*/
|
||||
protected GrowableByteBuffer interfaces = new GrowableByteBuffer();
|
||||
|
||||
/**
|
||||
* The fields of the class being built.
|
||||
*/
|
||||
protected GrowableByteBuffer fields = new GrowableByteBuffer();
|
||||
|
||||
/**
|
||||
* The methods of the class being built.
|
||||
*/
|
||||
protected GrowableByteBuffer methods = new GrowableByteBuffer();
|
||||
|
||||
int majorVersion;
|
||||
int minorVersion;
|
||||
int flags;
|
||||
int superclass;
|
||||
int nmethods, nfields, ninterfaces;
|
||||
|
||||
/**
|
||||
* Create a class builder.
|
||||
*
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
public ClassBuilder(BytePoolHelper<S, T> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
this.typeHelper = typeHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minor class file version.
|
||||
*
|
||||
* @param minorVersion the minor version number
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withMinorVersion(int minorVersion) {
|
||||
this.minorVersion = minorVersion;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the major class file version.
|
||||
*
|
||||
* @param majorVersion the major version number
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withMajorVersion(int majorVersion) {
|
||||
this.majorVersion = majorVersion;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class symbol
|
||||
*
|
||||
* @param thisClass the class symbol
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withThisClass(S thisClass) {
|
||||
this.thisClass = thisClass;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class access flags
|
||||
*
|
||||
* @param flags an array of {@code Flag}
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
@Override
|
||||
public C withFlags(Flag... flags) {
|
||||
for (Flag f : flags) {
|
||||
this.flags |= f.flag;
|
||||
}
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the superclass
|
||||
*
|
||||
* @param sup the superclass symbol
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withSuperclass(S sup) {
|
||||
this.superclass = poolHelper.putClass(sup);
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a super interface.
|
||||
*
|
||||
* @param sup an interface symbol
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withSuperinterface(S sup) {
|
||||
this.interfaces.writeChar(poolHelper.putClass(sup));
|
||||
ninterfaces++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field.
|
||||
*
|
||||
* @param name the name of the field
|
||||
* @param type the type descriptor of the field
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public final C withField(CharSequence name, T type) {
|
||||
return withField(name, type, FB -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field.
|
||||
*
|
||||
* @param name the name of the field
|
||||
* @param type the type descriptor of the field
|
||||
* @param fieldConfig access to the {@code FieldBuilder} to allow clients to
|
||||
* adjust flags, annotations and bytecode attributes on the field declaration
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withField(CharSequence name, T type, Consumer<? super FieldBuilder<S, T, byte[]>> fieldConfig) {
|
||||
FieldBuilder<S, T, byte[]> F = new FieldBuilder<>(name, type, poolHelper, typeHelper);
|
||||
fieldConfig.accept(F);
|
||||
F.build(fields);
|
||||
nfields++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a method
|
||||
*
|
||||
* @param name the name of the method
|
||||
* @param type the type descriptor of the method
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public final C withMethod(CharSequence name, T type) {
|
||||
return withMethod(name, type, MB -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a method
|
||||
*
|
||||
* @param name the name of the method
|
||||
* @param type the type descriptor of the method
|
||||
* @param methodConfig access to the {@code MethodBuilder} to allow clients to
|
||||
* adjust flags, annotations and bytecode attributes on the method declaration
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withMethod(CharSequence name, T type, Consumer<? super MethodBuilder<S, T, byte[]>> methodConfig) {
|
||||
MethodBuilder<S, T, byte[]> M = new MethodBuilder<>(thisClass, name, type, poolHelper, typeHelper);
|
||||
methodConfig.accept(M);
|
||||
M.build(methods);
|
||||
nmethods++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the constant pool into a byte array.
|
||||
*
|
||||
* @return a representation of this constant pool as a byte array
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public byte[] build() {
|
||||
((BytePoolHelper<S, T>)poolHelper).addAttributes(this);
|
||||
addAnnotations();
|
||||
int thisClassIdx = poolHelper.putClass(thisClass);
|
||||
byte[] poolBytes = poolHelper.entries();
|
||||
GrowableByteBuffer buf = new GrowableByteBuffer();
|
||||
buf.writeInt(0xCAFEBABE);
|
||||
buf.writeChar(minorVersion);
|
||||
buf.writeChar(majorVersion);
|
||||
buf.writeChar(poolHelper.size() + 1);
|
||||
buf.writeBytes(poolBytes);
|
||||
buf.writeChar(flags);
|
||||
buf.writeChar(thisClassIdx);
|
||||
buf.writeChar(superclass);
|
||||
buf.writeChar(ninterfaces);
|
||||
if (ninterfaces > 0) {
|
||||
buf.writeBytes(interfaces);
|
||||
}
|
||||
buf.writeChar(nfields);
|
||||
buf.writeBytes(fields);
|
||||
buf.writeChar(nmethods);
|
||||
buf.writeBytes(methods);
|
||||
buf.writeChar(nattrs);
|
||||
buf.writeBytes(attributes);
|
||||
return buf.bytes();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,115 +0,0 @@
|
||||
/*
|
||||
* 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.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Declaration (class, class member, ...) builder.
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <D> the type of this builder
|
||||
*/
|
||||
public class DeclBuilder<S, T, E, D extends DeclBuilder<S, T, E, D>>
|
||||
extends AttributeBuilder<S, T, E, D> {
|
||||
|
||||
/**
|
||||
* The access flags of the declaration, as bit flags.
|
||||
*/
|
||||
protected int flags;
|
||||
|
||||
AnnotationsBuilder<S, T, E> runtimeInvisibleAnnotations;
|
||||
AnnotationsBuilder<S, T, E> runtimeVisibleAnnotations;
|
||||
|
||||
/**
|
||||
* Create a declaration builder,
|
||||
*
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
DeclBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the class file flags for this declaration.
|
||||
*
|
||||
* @param flags the flags as {@code Flag} objects
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public D withFlags(Flag... flags) {
|
||||
for (Flag f : flags) {
|
||||
this.flags |= f.flag;
|
||||
}
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify, via bits, the class file flags for this declaration.
|
||||
*
|
||||
* @param flags the flags as bit settings
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public D withFlags(int flags) {
|
||||
withFlags(Flag.parse(flags));
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType) {
|
||||
getAnnotations(kind).withAnnotation(annoType, null);
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) {
|
||||
getAnnotations(kind).withAnnotation(annoType, annotations);
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
private AnnotationsBuilder<S, T, E> getAnnotations(AnnotationsBuilder.Kind kind) {
|
||||
switch (kind) {
|
||||
case RUNTIME_INVISIBLE:
|
||||
if (runtimeInvisibleAnnotations == null) {
|
||||
runtimeInvisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper);
|
||||
}
|
||||
return runtimeInvisibleAnnotations;
|
||||
case RUNTIME_VISIBLE:
|
||||
if (runtimeVisibleAnnotations == null) {
|
||||
runtimeVisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper);
|
||||
}
|
||||
return runtimeVisibleAnnotations;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
void addAnnotations() {
|
||||
if (runtimeVisibleAnnotations != null) {
|
||||
withAttribute("RuntimeVisibleAnnotations", runtimeVisibleAnnotations.build());
|
||||
}
|
||||
if (runtimeInvisibleAnnotations != null) {
|
||||
withAttribute("RuntimeInvisibleAnnotations", runtimeVisibleAnnotations.build());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Field builder.
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptor representation
|
||||
* @param <E> the type of pool entries
|
||||
*/
|
||||
public class FieldBuilder<S, T, E> extends MemberBuilder<S, T, E, FieldBuilder<S, T, E>> {
|
||||
public FieldBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(name, type, poolHelper, typeHelper);
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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.util.EnumSet;
|
||||
|
||||
public enum Flag {
|
||||
ACC_PUBLIC(0x0001),
|
||||
ACC_PROTECTED(0x0004),
|
||||
ACC_PRIVATE(0x0002),
|
||||
ACC_INTERFACE(0x0200),
|
||||
ACC_ENUM(0x4000),
|
||||
ACC_ANNOTATION(0x2000),
|
||||
ACC_SUPER(0x0020),
|
||||
ACC_ABSTRACT(0x0400),
|
||||
ACC_VOLATILE(0x0040),
|
||||
ACC_TRANSIENT(0x0080),
|
||||
ACC_SYNTHETIC(0x1000),
|
||||
ACC_STATIC(0x0008),
|
||||
ACC_FINAL(0x0010),
|
||||
ACC_SYNCHRONIZED(0x0020),
|
||||
ACC_BRIDGE(0x0040),
|
||||
ACC_VARARGS(0x0080),
|
||||
ACC_NATIVE(0x0100),
|
||||
ACC_STRICT(0x0800);
|
||||
|
||||
public int flag;
|
||||
|
||||
Flag(int flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
static Flag[] parse(int flagsMask) {
|
||||
EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
|
||||
for (Flag f : Flag.values()) {
|
||||
if ((f.flag & flagsMask) != 0) {
|
||||
flags.add(f);
|
||||
}
|
||||
}
|
||||
return flags.stream().toArray(Flag[]::new);
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* 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.util.function.Consumer;
|
||||
|
||||
public class GrowableByteBuffer {
|
||||
|
||||
public GrowableByteBuffer() {
|
||||
}
|
||||
|
||||
byte[] elems = new byte[64];
|
||||
int offset = 0;
|
||||
|
||||
public GrowableByteBuffer writeByte(int b) {
|
||||
return writeBytes(1, b);
|
||||
}
|
||||
|
||||
public GrowableByteBuffer writeChar(int x) {
|
||||
return writeBytes(2, x);
|
||||
}
|
||||
|
||||
public GrowableByteBuffer writeInt(int x) {
|
||||
return writeBytes(4, x);
|
||||
}
|
||||
|
||||
public GrowableByteBuffer writeFloat(float x) {
|
||||
return writeInt(Float.floatToIntBits(x));
|
||||
}
|
||||
|
||||
public GrowableByteBuffer writeLong(long x) {
|
||||
return writeBytes(8, x);
|
||||
}
|
||||
|
||||
public GrowableByteBuffer writeDouble(double x) {
|
||||
writeLong(Double.doubleToLongBits(x));
|
||||
return this;
|
||||
}
|
||||
|
||||
public GrowableByteBuffer writeBytes(byte[] barr) {
|
||||
expandIfNeeded(barr.length);
|
||||
System.arraycopy(barr, 0, elems, offset, barr.length);
|
||||
offset += barr.length;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GrowableByteBuffer writeBytes(GrowableByteBuffer bb) {
|
||||
expandIfNeeded(bb.offset);
|
||||
System.arraycopy(bb.elems, 0, elems, offset, bb.offset);
|
||||
offset += bb.offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GrowableByteBuffer withOffset(int offset, Consumer<GrowableByteBuffer> actions) {
|
||||
int prevOffset = this.offset;
|
||||
this.offset = offset;
|
||||
actions.accept(this);
|
||||
this.offset = prevOffset;
|
||||
return this;
|
||||
}
|
||||
|
||||
private GrowableByteBuffer writeBytes(int size, long x) {
|
||||
expandIfNeeded(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
elems[offset++] = (byte) ((x >> 8 * (size - i - 1)) & 0xFF);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void expandIfNeeded(int increment) {
|
||||
if (offset + increment > elems.length) {
|
||||
int newsize = elems.length * 2;
|
||||
while (offset + increment > newsize) {
|
||||
newsize *= 2;
|
||||
}
|
||||
byte[] newelems = new byte[newsize];
|
||||
System.arraycopy(elems, 0, newelems, 0, offset);
|
||||
elems = newelems;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] bytes() {
|
||||
byte[] bytes = new byte[offset];
|
||||
System.arraycopy(elems, 0, bytes, 0, offset);
|
||||
return bytes;
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
/*
|
||||
* 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.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
public class IsolatedMethodBuilder extends MethodBuilder<Class<?>, String, Object[]> {
|
||||
|
||||
public IsolatedMethodBuilder(Lookup lookup, String name, String type) {
|
||||
super(null, name, type, new IsolatedMethodPoolHelper(lookup), null);
|
||||
}
|
||||
|
||||
static class IsolatedMethodPoolHelper implements PoolHelper<Class<?>, String, Object[]> {
|
||||
Map<Object, Integer> constants = new HashMap<>();
|
||||
Lookup lookup;
|
||||
|
||||
private IsolatedMethodPoolHelper(Lookup lookup) {
|
||||
this.lookup = lookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putClass(Class<?> symbol) {
|
||||
return putIfAbsent(symbol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putFieldRef(Class<?> owner, CharSequence name, String type) {
|
||||
try {
|
||||
Field f = owner.getDeclaredField(name.toString()); //TODO: we should unreflect for a var handle
|
||||
return putIfAbsent(lookup.unreflectGetter(f));
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putMethodRef(Class<?> owner, CharSequence name, String type, boolean isInterface) {
|
||||
try {
|
||||
Method m = owner.getDeclaredMethod(name.toString()); //we should unreflect according to method vs. constructor
|
||||
//and static vs. private etc.
|
||||
return putIfAbsent(lookup.unreflect(m));
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putInt(int i) {
|
||||
return putIfAbsent(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putFloat(float f) {
|
||||
return putIfAbsent(f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putLong(long l) {
|
||||
return putIfAbsent(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putDouble(double d) {
|
||||
return putIfAbsent(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putString(String s) {
|
||||
return putIfAbsent(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putInvokeDynamic(CharSequence invokedName, String invokedType, Class<?> bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<Class<?>, String, Object[]>> staticArgs) {
|
||||
return 0; //???
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putDynamicConstant(CharSequence constName, String constType, Class<?> bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<Class<?>, String, Object[]>> staticArgs) {
|
||||
return 0; //???
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putHandle(int refKind, Class<?> owner, CharSequence name, String type) {
|
||||
return 0; //???
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putHandle(int refKind, Class<?> owner, CharSequence name, String type, boolean isInterface) {
|
||||
return 0; //???
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putMethodType(String s) {
|
||||
return 0; //???
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putUtf8(CharSequence s) {
|
||||
return putIfAbsent(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putType(String s) {
|
||||
return putIfAbsent(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return constants.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] entries() {
|
||||
return constants.keySet().toArray();
|
||||
}
|
||||
|
||||
int putIfAbsent(Object o) {
|
||||
int nextIndex = constants.size() + 1;
|
||||
Object res = constants.putIfAbsent(o, nextIndex);
|
||||
return res == null ?
|
||||
nextIndex : (Integer)res;
|
||||
}
|
||||
}
|
||||
|
||||
public Object[] entries() {
|
||||
return poolHelper.entries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] build() {
|
||||
byte[] arr = super.build();
|
||||
int codelength_offset = 2 + 2 + 2 + 2 +
|
||||
2 + 4 + 2 + 2;
|
||||
int code_offset = codelength_offset + 4;
|
||||
int length = ByteBuffer.wrap(arr).getInt(codelength_offset);
|
||||
byte[] opcodes = new byte[length];
|
||||
System.arraycopy(arr, code_offset, opcodes, 0, length);
|
||||
return opcodes;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
IsolatedMethodBuilder imb = new IsolatedMethodBuilder(MethodHandles.lookup(), "foo", "(java/lang/String;)I");
|
||||
imb.withCode(C ->
|
||||
C.aload_0()
|
||||
.invokevirtual(String.class, "length", "()I", false)
|
||||
.ireturn());
|
||||
byte[] opcodes = imb.build();
|
||||
System.out.println(Arrays.toString(opcodes));
|
||||
}
|
||||
}
|
@ -1,712 +0,0 @@
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MacroCodeBuilder<S, T, E, C extends MacroCodeBuilder<S, T, E, C>> extends CodeBuilder<S, T, E, C> {
|
||||
|
||||
JumpMode jumpMode = JumpMode.NARROW;
|
||||
|
||||
Map<CharSequence, Integer> labels = new HashMap<>();
|
||||
List<PendingJump> pendingJumps = new LinkedList<>();
|
||||
|
||||
class PendingJump {
|
||||
CharSequence label;
|
||||
int pc;
|
||||
|
||||
PendingJump(CharSequence label, int pc) {
|
||||
this.label = label;
|
||||
this.pc = pc;
|
||||
}
|
||||
|
||||
boolean resolve(CharSequence label, int offset) {
|
||||
if (this.label.equals(label)) {
|
||||
//patch offset
|
||||
code.withOffset(pc + 1, buf -> emitOffset(buf, jumpMode, offset - pc));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum InvocationKind {
|
||||
INVOKESTATIC,
|
||||
INVOKEVIRTUAL,
|
||||
INVOKESPECIAL,
|
||||
INVOKEINTERFACE;
|
||||
}
|
||||
|
||||
public enum FieldAccessKind {
|
||||
STATIC,
|
||||
INSTANCE;
|
||||
}
|
||||
|
||||
public enum CondKind {
|
||||
EQ(0),
|
||||
NE(1),
|
||||
LT(2),
|
||||
GE(3),
|
||||
GT(4),
|
||||
LE(5);
|
||||
|
||||
int offset;
|
||||
|
||||
CondKind(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public CondKind negate() {
|
||||
switch (this) {
|
||||
case EQ:
|
||||
return NE;
|
||||
case NE:
|
||||
return EQ;
|
||||
case LT:
|
||||
return GE;
|
||||
case GE:
|
||||
return LT;
|
||||
case GT:
|
||||
return LE;
|
||||
case LE:
|
||||
return GT;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown cond");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class WideJumpException extends RuntimeException {
|
||||
static final long serialVersionUID = 42L;
|
||||
}
|
||||
|
||||
public MacroCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
|
||||
super(methodBuilder);
|
||||
}
|
||||
|
||||
public C load(TypeTag type, int n) {
|
||||
if (type == TypeTag.Q) {
|
||||
return vload(n);
|
||||
} else {
|
||||
switch (n) {
|
||||
case 0:
|
||||
return emitOp(Opcode.ILOAD_0.at(type, 4));
|
||||
case 1:
|
||||
return emitOp(Opcode.ILOAD_1.at(type, 4));
|
||||
case 2:
|
||||
return emitOp(Opcode.ILOAD_2.at(type, 4));
|
||||
case 3:
|
||||
return emitOp(Opcode.ILOAD_3.at(type, 4));
|
||||
default:
|
||||
return emitWideIfNeeded(Opcode.ILOAD.at(type), n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public C store(TypeTag type, int n) {
|
||||
if (type == TypeTag.Q) {
|
||||
return vstore(n);
|
||||
} else {
|
||||
switch (n) {
|
||||
case 0:
|
||||
return emitOp(Opcode.ISTORE_0.at(type, 4));
|
||||
case 1:
|
||||
return emitOp(Opcode.ISTORE_1.at(type, 4));
|
||||
case 2:
|
||||
return emitOp(Opcode.ISTORE_2.at(type, 4));
|
||||
case 3:
|
||||
return emitOp(Opcode.ISTORE_3.at(type, 4));
|
||||
default:
|
||||
return emitWideIfNeeded(Opcode.ISTORE.at(type), n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public C arrayload(TypeTag type) {
|
||||
return emitOp(Opcode.IALOAD.at(type));
|
||||
}
|
||||
|
||||
public C arraystore(TypeTag type, int n) {
|
||||
return emitOp(Opcode.IASTORE.at(type));
|
||||
}
|
||||
|
||||
public C const_(int i) {
|
||||
switch (i) {
|
||||
case -1:
|
||||
return iconst_m1();
|
||||
case 0:
|
||||
return iconst_0();
|
||||
case 1:
|
||||
return iconst_1();
|
||||
case 2:
|
||||
return iconst_2();
|
||||
case 3:
|
||||
return iconst_3();
|
||||
case 4:
|
||||
return iconst_4();
|
||||
case 5:
|
||||
return iconst_5();
|
||||
default:
|
||||
if (i > 0 && i <= Byte.MAX_VALUE) {
|
||||
return bipush(i);
|
||||
} else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
|
||||
return sipush(i);
|
||||
} else {
|
||||
return ldc(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public C const_(long l) {
|
||||
if (l == 0) {
|
||||
return lconst_0();
|
||||
} else if (l == 1) {
|
||||
return lconst_1();
|
||||
} else {
|
||||
return ldc(l);
|
||||
}
|
||||
}
|
||||
|
||||
public C const_(float f) {
|
||||
if (f == 0) {
|
||||
return fconst_0();
|
||||
} else if (f == 1) {
|
||||
return fconst_1();
|
||||
} else if (f == 2) {
|
||||
return fconst_2();
|
||||
} else {
|
||||
return ldc(f);
|
||||
}
|
||||
}
|
||||
|
||||
public C const_(double d) {
|
||||
if (d == 0) {
|
||||
return dconst_0();
|
||||
} else if (d == 1) {
|
||||
return dconst_1();
|
||||
} else {
|
||||
return ldc(d);
|
||||
}
|
||||
}
|
||||
|
||||
public C getfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
|
||||
switch (fak) {
|
||||
case INSTANCE:
|
||||
return getfield(owner, name, type);
|
||||
case STATIC:
|
||||
return getstatic(owner, name, type);
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public C putfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
|
||||
switch (fak) {
|
||||
case INSTANCE:
|
||||
return putfield(owner, name, type);
|
||||
case STATIC:
|
||||
return putstatic(owner, name, type);
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public C invoke(InvocationKind ik, S owner, CharSequence name, T type, boolean isInterface) {
|
||||
switch (ik) {
|
||||
case INVOKESTATIC:
|
||||
return invokestatic(owner, name, type, isInterface);
|
||||
case INVOKEVIRTUAL:
|
||||
return invokevirtual(owner, name, type, isInterface);
|
||||
case INVOKESPECIAL:
|
||||
return invokespecial(owner, name, type, isInterface);
|
||||
case INVOKEINTERFACE:
|
||||
if (!isInterface) throw new AssertionError();
|
||||
return invokeinterface(owner, name, type);
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public C add(TypeTag type) {
|
||||
return emitOp(Opcode.IADD.at(type));
|
||||
}
|
||||
|
||||
public C sub(TypeTag type) {
|
||||
return emitOp(Opcode.ISUB.at(type));
|
||||
}
|
||||
|
||||
public C mul(TypeTag type) {
|
||||
return emitOp(Opcode.IMUL.at(type));
|
||||
}
|
||||
|
||||
public C div(TypeTag type) {
|
||||
return emitOp(Opcode.IDIV.at(type));
|
||||
}
|
||||
|
||||
public C rem(TypeTag type) {
|
||||
return emitOp(Opcode.IREM.at(type));
|
||||
}
|
||||
|
||||
public C neg(TypeTag type) {
|
||||
return emitOp(Opcode.INEG.at(type));
|
||||
}
|
||||
|
||||
public C shl(TypeTag type) {
|
||||
return emitOp(Opcode.ISHL.at(type));
|
||||
}
|
||||
|
||||
public C shr(TypeTag type) {
|
||||
return emitOp(Opcode.ISHR.at(type));
|
||||
}
|
||||
|
||||
public C ushr(TypeTag type) {
|
||||
return emitOp(Opcode.ISHR.at(type));
|
||||
}
|
||||
|
||||
public C and(TypeTag type) {
|
||||
return emitOp(Opcode.IAND.at(type));
|
||||
}
|
||||
|
||||
public C or(TypeTag type) {
|
||||
return emitOp(Opcode.IOR.at(type));
|
||||
}
|
||||
|
||||
public C xor(TypeTag type) {
|
||||
return emitOp(Opcode.IXOR.at(type));
|
||||
}
|
||||
|
||||
public C return_(TypeTag type) {
|
||||
switch (type) {
|
||||
case V:
|
||||
return return_();
|
||||
case Q:
|
||||
return vreturn();
|
||||
default:
|
||||
return emitOp(Opcode.IRETURN.at(type));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelledTypedBuilder typed(TypeTag typeTag) {
|
||||
return super.typed(typeTag, _unused -> new LabelledTypedBuilder());
|
||||
}
|
||||
|
||||
public class LabelledTypedBuilder extends TypedBuilder {
|
||||
public C if_acmpeq(CharSequence target) {
|
||||
return ifcmp(TypeTag.A, CondKind.EQ, target);
|
||||
}
|
||||
|
||||
public C if_acmpne(CharSequence target) {
|
||||
return ifcmp(TypeTag.A, CondKind.NE, target);
|
||||
}
|
||||
}
|
||||
|
||||
public C conv(TypeTag from, TypeTag to) {
|
||||
switch (from) {
|
||||
case B:
|
||||
case C:
|
||||
case S:
|
||||
switch (to) {
|
||||
case J:
|
||||
return i2l();
|
||||
case F:
|
||||
return i2f();
|
||||
case D:
|
||||
return i2d();
|
||||
}
|
||||
break;
|
||||
case I:
|
||||
switch (to) {
|
||||
case J:
|
||||
return i2l();
|
||||
case F:
|
||||
return i2f();
|
||||
case D:
|
||||
return i2d();
|
||||
case B:
|
||||
return i2b();
|
||||
case C:
|
||||
return i2c();
|
||||
case S:
|
||||
return i2s();
|
||||
}
|
||||
break;
|
||||
case J:
|
||||
switch (to) {
|
||||
case I:
|
||||
return l2i();
|
||||
case F:
|
||||
return l2f();
|
||||
case D:
|
||||
return l2d();
|
||||
}
|
||||
break;
|
||||
case F:
|
||||
switch (to) {
|
||||
case I:
|
||||
return f2i();
|
||||
case J:
|
||||
return f2l();
|
||||
case D:
|
||||
return f2d();
|
||||
}
|
||||
break;
|
||||
case D:
|
||||
switch (to) {
|
||||
case I:
|
||||
return d2i();
|
||||
case J:
|
||||
return d2l();
|
||||
case F:
|
||||
return d2f();
|
||||
}
|
||||
break;
|
||||
}
|
||||
//no conversion is necessary - do nothing!
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
public C if_null(CharSequence label) {
|
||||
return emitCondJump(Opcode.IF_NULL, Opcode.IF_NONNULL, label);
|
||||
}
|
||||
|
||||
public C if_nonnull(CharSequence label) {
|
||||
return emitCondJump(Opcode.IF_NONNULL, Opcode.IF_NULL, label);
|
||||
}
|
||||
|
||||
public C ifcmp(TypeTag type, CondKind cond, CharSequence label) {
|
||||
switch (type) {
|
||||
case I:
|
||||
return emitCondJump(Opcode.IF_ICMPEQ, cond, label);
|
||||
case A:
|
||||
return emitCondJump(Opcode.IF_ACMPEQ, cond, label);
|
||||
case J:
|
||||
return lcmp().emitCondJump(Opcode.IFEQ, cond, label);
|
||||
case D:
|
||||
return dcmpg().emitCondJump(Opcode.IFEQ, cond, label);
|
||||
case F:
|
||||
return fcmpg().emitCondJump(Opcode.IFEQ, cond, label);
|
||||
default:
|
||||
throw new IllegalArgumentException("Bad cmp type");
|
||||
}
|
||||
}
|
||||
|
||||
public C goto_(CharSequence label) {
|
||||
emitOp(jumpMode == JumpMode.NARROW ? Opcode.GOTO_ : Opcode.GOTO_W);
|
||||
emitOffset(code, jumpMode, labelOffset(label));
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
protected int labelOffset(CharSequence label) {
|
||||
int pc = code.offset - 1;
|
||||
Integer labelPc = labels.get(label);
|
||||
if (labelPc == null) {
|
||||
addPendingJump(label, pc);
|
||||
}
|
||||
return labelPc == null ? 0 : (labelPc - pc);
|
||||
}
|
||||
|
||||
public C label(CharSequence s) {
|
||||
int pc = code.offset;
|
||||
Object old = labels.put(s, pc);
|
||||
if (old != null) {
|
||||
throw new IllegalStateException("label already exists");
|
||||
}
|
||||
resolveJumps(s, pc);
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
//FIXME: address this jumpy mess - i.e. offset and state update work against each other!
|
||||
public C emitCondJump(Opcode opcode, CondKind ck, CharSequence label) {
|
||||
return emitCondJump(opcode.at(ck), opcode.at(ck.negate()), label);
|
||||
}
|
||||
|
||||
public C emitCondJump(Opcode pos, Opcode neg, CharSequence label) {
|
||||
if (jumpMode == JumpMode.NARROW) {
|
||||
emitOp(pos);
|
||||
emitOffset(code, jumpMode, labelOffset(label));
|
||||
} else {
|
||||
emitOp(neg);
|
||||
emitOffset(code, JumpMode.NARROW, 8);
|
||||
goto_w(labelOffset(label));
|
||||
}
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
void addPendingJump(CharSequence label, int pc) {
|
||||
pendingJumps.add(new PendingJump(label, pc));
|
||||
}
|
||||
|
||||
void resolveJumps(CharSequence label, int pc) {
|
||||
Iterator<PendingJump> jumpsIt = pendingJumps.iterator();
|
||||
while (jumpsIt.hasNext()) {
|
||||
PendingJump jump = jumpsIt.next();
|
||||
if (jump.resolve(label, pc)) {
|
||||
jumpsIt.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
|
||||
if (jumpMode == JumpMode.NARROW && (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE)) {
|
||||
throw new WideJumpException();
|
||||
}
|
||||
super.emitOffset(buf, jumpMode, offset);
|
||||
}
|
||||
|
||||
public C jsr(CharSequence label) {
|
||||
emitOp(jumpMode == JumpMode.NARROW ? Opcode.JSR : Opcode.JSR_W);
|
||||
emitOffset(code, jumpMode, labelOffset(label));
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
|
||||
int start = code.offset;
|
||||
tryBlock.accept((C) this);
|
||||
int end = code.offset;
|
||||
CatchBuilder catchBuilder = makeCatchBuilder(start, end);
|
||||
catchBlocks.accept(catchBuilder);
|
||||
catchBuilder.build();
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
code.offset = 0;
|
||||
catchers.offset = 0;
|
||||
ncatchers = 0;
|
||||
labels.clear();
|
||||
pendingJumps = null;
|
||||
}
|
||||
|
||||
protected CatchBuilder makeCatchBuilder(int start, int end) {
|
||||
return new CatchBuilder(start, end);
|
||||
}
|
||||
|
||||
public class CatchBuilder {
|
||||
int start, end;
|
||||
|
||||
String endLabel = labelName();
|
||||
|
||||
Map<S, Consumer<? super C>> catchers = new LinkedHashMap<>();
|
||||
public Consumer<? super C> finalizer;
|
||||
List<Integer> pendingGaps = new ArrayList<>();
|
||||
|
||||
public CatchBuilder(int start, int end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
|
||||
catchers.put(exc, catcher);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CatchBuilder withFinally(Consumer<? super C> finalizer) {
|
||||
this.finalizer = finalizer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void build() {
|
||||
if (finalizer != null) {
|
||||
finalizer.accept((C) MacroCodeBuilder.this);
|
||||
}
|
||||
goto_(endLabel);
|
||||
for (Map.Entry<S, Consumer<? super C>> catcher_entry : catchers.entrySet()) {
|
||||
emitCatch(catcher_entry.getKey(), catcher_entry.getValue());
|
||||
}
|
||||
if (finalizer != null) {
|
||||
emitFinalizer();
|
||||
}
|
||||
resolveJumps(endLabel, code.offset);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void emitCatch(S exc, Consumer<? super C> catcher) {
|
||||
int offset = code.offset;
|
||||
MacroCodeBuilder.this.withCatch(exc, start, end, offset);
|
||||
catcher.accept((C) MacroCodeBuilder.this);
|
||||
if (finalizer != null) {
|
||||
int startFinalizer = code.offset;
|
||||
finalizer.accept((C) MacroCodeBuilder.this);
|
||||
pendingGaps.add(startFinalizer);
|
||||
pendingGaps.add(code.offset);
|
||||
}
|
||||
goto_(endLabel);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void emitFinalizer() {
|
||||
int offset = code.offset;
|
||||
pop();
|
||||
for (int i = 0; i < pendingGaps.size(); i += 2) {
|
||||
MacroCodeBuilder.this.withCatch(null, pendingGaps.get(i), pendingGaps.get(i + 1), offset);
|
||||
}
|
||||
MacroCodeBuilder.this.withCatch(null, start, end, offset);
|
||||
finalizer.accept((C) MacroCodeBuilder.this);
|
||||
}
|
||||
|
||||
// @SuppressWarnings("unchecked")
|
||||
// CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
|
||||
// int offset = code.offset;
|
||||
// MacroCodeBuilder.this.withCatch(exc, start, end, offset);
|
||||
// catcher.accept((C)MacroCodeBuilder.this);
|
||||
// return this;
|
||||
// }
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// CatchBuilder withFinally(Consumer<? super C> catcher) {
|
||||
// int offset = code.offset;
|
||||
// MacroCodeBuilder.this.withCatch(null, start, end, offset);
|
||||
// catcher.accept((C)MacroCodeBuilder.this);
|
||||
// return this;
|
||||
// }
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public C switch_(Consumer<? super SwitchBuilder> consumer) {
|
||||
int start = code.offset;
|
||||
SwitchBuilder sb = makeSwitchBuilder();
|
||||
consumer.accept(sb);
|
||||
int nlabels = sb.cases.size();
|
||||
switch (sb.switchCode()) {
|
||||
case LOOKUPSWITCH: {
|
||||
int[] lookupOffsets = new int[nlabels * 2];
|
||||
int i = 0;
|
||||
for (Integer v : sb.cases.keySet()) {
|
||||
lookupOffsets[i] = v;
|
||||
i += 2;
|
||||
}
|
||||
lookupswitch(0, lookupOffsets);
|
||||
//backpatch lookup
|
||||
int curr = code.offset - (8 * nlabels) - 8;
|
||||
int defaultOffset = code.offset - start;
|
||||
code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
|
||||
sb.defaultCase.accept((C) this);
|
||||
curr += 12;
|
||||
for (Consumer<? super C> case_ : sb.cases.values()) {
|
||||
int offset = code.offset;
|
||||
code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset - start));
|
||||
case_.accept((C) this);
|
||||
curr += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TABLESWITCH: {
|
||||
int[] tableOffsets = new int[sb.hi - sb.lo + 1];
|
||||
tableswitch(sb.lo, sb.hi, 0, tableOffsets);
|
||||
//backpatch table
|
||||
int curr = code.offset - (4 * tableOffsets.length) - 12;
|
||||
int defaultOffset = code.offset - start;
|
||||
code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
|
||||
sb.defaultCase.accept((C) this);
|
||||
curr += 12;
|
||||
int lastCasePc = -1;
|
||||
for (int i = sb.lo; i <= sb.hi; i++) {
|
||||
Consumer<? super C> case_ = sb.cases.get(i);
|
||||
if (case_ != null) {
|
||||
lastCasePc = code.offset;
|
||||
case_.accept((C) this);
|
||||
}
|
||||
int offset = lastCasePc - start;
|
||||
code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset));
|
||||
curr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
resolveJumps(sb.endLabel, code.offset);
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
private static int labelCount = 0;
|
||||
|
||||
String labelName() {
|
||||
return "label" + labelCount++;
|
||||
}
|
||||
|
||||
protected SwitchBuilder makeSwitchBuilder() {
|
||||
return new SwitchBuilder();
|
||||
}
|
||||
|
||||
public class SwitchBuilder {
|
||||
Map<Integer, Consumer<? super C>> cases = new TreeMap<>();
|
||||
int lo = Integer.MAX_VALUE;
|
||||
int hi = Integer.MIN_VALUE;
|
||||
String endLabel = labelName();
|
||||
|
||||
public Consumer<? super C> defaultCase;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
|
||||
if (value > hi) {
|
||||
hi = value;
|
||||
}
|
||||
if (value < lo) {
|
||||
lo = value;
|
||||
}
|
||||
if (!fallthrough) {
|
||||
Consumer<? super C> prevCase = case_;
|
||||
case_ = C -> {
|
||||
prevCase.accept(C);
|
||||
C.goto_(endLabel);
|
||||
};
|
||||
}
|
||||
cases.put(value, case_);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
|
||||
if (this.defaultCase != null) {
|
||||
throw new IllegalStateException("default already set");
|
||||
}
|
||||
this.defaultCase = defaultCase;
|
||||
return this;
|
||||
}
|
||||
|
||||
Opcode switchCode() {
|
||||
int nlabels = cases.size();
|
||||
// Determine whether to issue a tableswitch or a lookupswitch
|
||||
// instruction.
|
||||
long table_space_cost = 4 + ((long) hi - lo + 1); // words
|
||||
long lookup_space_cost = 3 + 2 * (long) nlabels;
|
||||
return
|
||||
nlabels > 0 &&
|
||||
table_space_cost <= lookup_space_cost
|
||||
?
|
||||
Opcode.TABLESWITCH : Opcode.LOOKUPSWITCH;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Class member builder.
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <M> the type of this builder
|
||||
*/
|
||||
public class MemberBuilder<S, T, E, M extends MemberBuilder<S, T, E, M>> extends DeclBuilder<S, T, E, M> {
|
||||
|
||||
CharSequence name;
|
||||
T desc;
|
||||
|
||||
/**
|
||||
* Create a member builder.
|
||||
*
|
||||
* @param name the name of the class member
|
||||
* @param type the type descriptor of the class member
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
MemberBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
this.name = name;
|
||||
this.desc = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the member.
|
||||
*
|
||||
* @param buf the {@code GrowableByteBuffer} to build the member into
|
||||
*/
|
||||
protected void build(GrowableByteBuffer buf) {
|
||||
addAnnotations();
|
||||
buf.writeChar(flags);
|
||||
buf.writeChar(poolHelper.putUtf8(name));
|
||||
buf.writeChar(poolHelper.putType(desc));
|
||||
buf.writeChar(nattrs);
|
||||
buf.writeBytes(attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the member.
|
||||
*
|
||||
* @return a byte array representation of the member
|
||||
*/
|
||||
protected byte[] build() {
|
||||
GrowableByteBuffer buf = new GrowableByteBuffer();
|
||||
addAnnotations();
|
||||
build(buf);
|
||||
return buf.bytes();
|
||||
}
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
* 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 jdk.experimental.bytecode.CodeBuilder.JumpMode;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class MethodBuilder<S, T, E> extends MemberBuilder<S, T, E, MethodBuilder<S, T, E>> {
|
||||
|
||||
S thisClass;
|
||||
ParameterAnnotationsBuilder runtimeVisibleParameterAnnotations;
|
||||
ParameterAnnotationsBuilder runtimeInvisibleParameterAnnotations;
|
||||
|
||||
public MethodBuilder(S thisClass, CharSequence name, T type, PoolHelper<S, T, E> pool, TypeHelper<S, T> typeHelper) {
|
||||
super(name, type, pool, typeHelper);
|
||||
this.thisClass = thisClass;
|
||||
}
|
||||
|
||||
public <C extends CodeBuilder<S, T, E, ?>> MethodBuilder<S, T, E> withCode(Function<? super MethodBuilder<S, T, E>, ? extends C> func,
|
||||
Consumer<? super C> code) {
|
||||
C codeBuilder = func.apply(this);
|
||||
int start = attributes.offset;
|
||||
try {
|
||||
code.accept(codeBuilder);
|
||||
} catch (MacroCodeBuilder.WideJumpException ex) {
|
||||
//wide jumps! Redo the code
|
||||
((MacroCodeBuilder<S, T, E, ?>) codeBuilder).jumpMode = JumpMode.WIDE;
|
||||
((MacroCodeBuilder<S, T, E, ?>) codeBuilder).clear();
|
||||
code.accept(codeBuilder);
|
||||
}
|
||||
|
||||
attributes.writeChar(poolHelper.putUtf8("Code"));
|
||||
attributes.writeInt(0);
|
||||
codeBuilder.build(attributes);
|
||||
int length = attributes.offset - start;
|
||||
//avoid using lambda here
|
||||
int prevOffset = attributes.offset;
|
||||
try {
|
||||
attributes.offset = start + 2;
|
||||
attributes.writeInt(length - 6);
|
||||
} finally {
|
||||
attributes.offset = prevOffset;
|
||||
}
|
||||
nattrs++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MethodBuilder<S, T, E> withCode(Consumer<? super CodeBuilder<S, T, E, ?>> code) {
|
||||
return withCode(CodeBuilder::new, code);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"varargs", "unchecked"})
|
||||
public MethodBuilder<S, T, E> withExceptions(S... exceptions) {
|
||||
attributes.writeChar(poolHelper.putUtf8("Exceptions"));
|
||||
attributes.writeInt(2 + (2 * exceptions.length));
|
||||
attributes.writeChar(exceptions.length);
|
||||
for (S exception : exceptions) {
|
||||
attributes.writeChar(poolHelper.putClass(exception));
|
||||
}
|
||||
nattrs++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType) {
|
||||
getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) {
|
||||
getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, annotations);
|
||||
return this;
|
||||
}
|
||||
|
||||
private ParameterAnnotationsBuilder getParameterAnnotations(AnnotationsBuilder.Kind kind) {
|
||||
switch (kind) {
|
||||
case RUNTIME_INVISIBLE:
|
||||
if (runtimeInvisibleParameterAnnotations == null) {
|
||||
runtimeInvisibleParameterAnnotations = new ParameterAnnotationsBuilder();
|
||||
}
|
||||
return runtimeInvisibleParameterAnnotations;
|
||||
case RUNTIME_VISIBLE:
|
||||
if (runtimeVisibleParameterAnnotations == null) {
|
||||
runtimeVisibleParameterAnnotations = new ParameterAnnotationsBuilder();
|
||||
}
|
||||
return runtimeVisibleParameterAnnotations;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
class ParameterAnnotationsBuilder {
|
||||
|
||||
GrowableByteBuffer parameterAnnos = new GrowableByteBuffer();
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
AnnotationsBuilder<S, T, E>[] builders = new AnnotationsBuilder[nparams()];
|
||||
|
||||
ParameterAnnotationsBuilder() {
|
||||
for (int i = 0; i < builders.length; i++) {
|
||||
builders[i] = new AnnotationsBuilder<>(poolHelper, typeHelper);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] build() {
|
||||
parameterAnnos.writeByte(builders.length);
|
||||
for (AnnotationsBuilder<S, T, E> builder : builders) {
|
||||
parameterAnnos.writeBytes(builder.build());
|
||||
}
|
||||
return parameterAnnos.bytes();
|
||||
}
|
||||
|
||||
int nparams() {
|
||||
Iterator<T> paramsIt = typeHelper.parameterTypes(desc);
|
||||
int nparams = 0;
|
||||
while (paramsIt.hasNext()) {
|
||||
paramsIt.next();
|
||||
nparams++;
|
||||
}
|
||||
return nparams;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void addAnnotations() {
|
||||
super.addAnnotations();
|
||||
if (runtimeInvisibleParameterAnnotations != null) {
|
||||
withAttribute("RuntimeInvisibleParameterAnnotations", runtimeInvisibleParameterAnnotations.build());
|
||||
}
|
||||
if (runtimeVisibleParameterAnnotations != null) {
|
||||
withAttribute("RuntimeVisibleParameterAnnotations", runtimeVisibleParameterAnnotations.build());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
/*
|
||||
* 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 jdk.experimental.bytecode.MacroCodeBuilder.CondKind;
|
||||
|
||||
public enum Opcode {
|
||||
|
||||
NOP(0),
|
||||
ACONST_NULL(1),
|
||||
ICONST_M1(2),
|
||||
ICONST_0(3),
|
||||
ICONST_1(4),
|
||||
ICONST_2(5),
|
||||
ICONST_3(6),
|
||||
ICONST_4(7),
|
||||
ICONST_5(8),
|
||||
LCONST_0(9),
|
||||
LCONST_1(10),
|
||||
FCONST_0(11),
|
||||
FCONST_1(12),
|
||||
FCONST_2(13),
|
||||
DCONST_0(14),
|
||||
DCONST_1(15),
|
||||
BIPUSH(16),
|
||||
SIPUSH(17),
|
||||
LDC(18),
|
||||
LDC_W(19),
|
||||
LDC2_W(20),
|
||||
ILOAD(21),
|
||||
LLOAD(22),
|
||||
FLOAD(23),
|
||||
DLOAD(24),
|
||||
ALOAD(25),
|
||||
ILOAD_0(26),
|
||||
ILOAD_1(27),
|
||||
ILOAD_2(28),
|
||||
ILOAD_3(29),
|
||||
LLOAD_0(30),
|
||||
LLOAD_1(31),
|
||||
LLOAD_2(32),
|
||||
LLOAD_3(33),
|
||||
FLOAD_0(34),
|
||||
FLOAD_1(35),
|
||||
FLOAD_2(36),
|
||||
FLOAD_3(37),
|
||||
DLOAD_0(38),
|
||||
DLOAD_1(39),
|
||||
DLOAD_2(40),
|
||||
DLOAD_3(41),
|
||||
ALOAD_0(42),
|
||||
ALOAD_1(43),
|
||||
ALOAD_2(44),
|
||||
ALOAD_3(45),
|
||||
IALOAD(46),
|
||||
LALOAD(47),
|
||||
FALOAD(48),
|
||||
DALOAD(49),
|
||||
AALOAD(50),
|
||||
BALOAD(51),
|
||||
CALOAD(52),
|
||||
SALOAD(53),
|
||||
ISTORE(54),
|
||||
LSTORE(55),
|
||||
FSTORE(56),
|
||||
DSTORE(57),
|
||||
ASTORE(58),
|
||||
ISTORE_0(59),
|
||||
ISTORE_1(60),
|
||||
ISTORE_2(61),
|
||||
ISTORE_3(62),
|
||||
LSTORE_0(63),
|
||||
LSTORE_1(64),
|
||||
LSTORE_2(65),
|
||||
LSTORE_3(66),
|
||||
FSTORE_0(67),
|
||||
FSTORE_1(68),
|
||||
FSTORE_2(69),
|
||||
FSTORE_3(70),
|
||||
DSTORE_0(71),
|
||||
DSTORE_1(72),
|
||||
DSTORE_2(73),
|
||||
DSTORE_3(74),
|
||||
ASTORE_0(75),
|
||||
ASTORE_1(76),
|
||||
ASTORE_2(77),
|
||||
ASTORE_3(78),
|
||||
IASTORE(79),
|
||||
LASTORE(80),
|
||||
FASTORE(81),
|
||||
DASTORE(82),
|
||||
AASTORE(83),
|
||||
BASTORE(84),
|
||||
CASTORE(85),
|
||||
SASTORE(86),
|
||||
POP(87),
|
||||
POP2(88),
|
||||
DUP(89),
|
||||
DUP_X1(90),
|
||||
DUP_X2(91),
|
||||
DUP2(92),
|
||||
DUP2_X1(93),
|
||||
DUP2_X2(94),
|
||||
SWAP(95),
|
||||
IADD(96),
|
||||
LADD(97),
|
||||
FADD(98),
|
||||
DADD(99),
|
||||
ISUB(100),
|
||||
LSUB(101),
|
||||
FSUB(102),
|
||||
DSUB(103),
|
||||
IMUL(104),
|
||||
LMUL(105),
|
||||
FMUL(106),
|
||||
DMUL(107),
|
||||
IDIV(108),
|
||||
LDIV(109),
|
||||
FDIV(110),
|
||||
DDIV(111),
|
||||
IREM(112),
|
||||
LREM(113),
|
||||
FREM(114),
|
||||
DREM(115),
|
||||
INEG(116),
|
||||
LNEG(117),
|
||||
FNEG(118),
|
||||
DNEG(119),
|
||||
ISHL(120),
|
||||
LSHL(121),
|
||||
ISHR(122),
|
||||
LSHR(123),
|
||||
IUSHR(124),
|
||||
LUSHR(125),
|
||||
IAND(126),
|
||||
LAND(127),
|
||||
IOR(128),
|
||||
LOR(129),
|
||||
IXOR(130),
|
||||
LXOR(131),
|
||||
IINC(132),
|
||||
I2L(133),
|
||||
I2F(134),
|
||||
I2D(135),
|
||||
L2I(136),
|
||||
L2F(137),
|
||||
L2D(138),
|
||||
F2I(139),
|
||||
F2L(140),
|
||||
F2D(141),
|
||||
D2I(142),
|
||||
D2L(143),
|
||||
D2F(144),
|
||||
I2B(145),
|
||||
I2C(146),
|
||||
I2S(147),
|
||||
LCMP(148),
|
||||
FCMPL(149),
|
||||
FCMPG(150),
|
||||
DCMPL(151),
|
||||
DCMPG(152),
|
||||
IFEQ(153),
|
||||
IFNE(154),
|
||||
IFLT(155),
|
||||
IFGE(156),
|
||||
IFGT(157),
|
||||
IFLE(158),
|
||||
IF_ICMPEQ(159),
|
||||
IF_ICMPNE(160),
|
||||
IF_ICMPLT(161),
|
||||
IF_ICMPGE(162),
|
||||
IF_ICMPGT(163),
|
||||
IF_ICMPLE(164),
|
||||
IF_ACMPEQ(165),
|
||||
IF_ACMPNE(166),
|
||||
GOTO_(167),
|
||||
JSR(168),
|
||||
RET(169),
|
||||
TABLESWITCH(170),
|
||||
LOOKUPSWITCH(171),
|
||||
IRETURN(172),
|
||||
LRETURN(173),
|
||||
FRETURN(174),
|
||||
DRETURN(175),
|
||||
ARETURN(176),
|
||||
RETURN(177),
|
||||
GETSTATIC(178),
|
||||
PUTSTATIC(179),
|
||||
GETFIELD(180),
|
||||
PUTFIELD(181),
|
||||
INVOKEVIRTUAL(182),
|
||||
INVOKESPECIAL(183),
|
||||
INVOKESTATIC(184),
|
||||
INVOKEINTERFACE(185),
|
||||
INVOKEDYNAMIC(186),
|
||||
NEW(187),
|
||||
NEWARRAY(188),
|
||||
ANEWARRAY(189),
|
||||
ARRAYLENGTH(190),
|
||||
ATHROW(191),
|
||||
CHECKCAST(192),
|
||||
INSTANCEOF(193),
|
||||
MONITORENTER(194),
|
||||
MONITOREXIT(195),
|
||||
WIDE(196),
|
||||
MULTIANEWARRAY(197),
|
||||
IF_NULL(198),
|
||||
IF_NONNULL(199),
|
||||
GOTO_W(200),
|
||||
JSR_W(201),
|
||||
VLOAD(203),
|
||||
VSTORE(204),
|
||||
VALOAD(205),
|
||||
VASTORE(206),
|
||||
VNEW(207),
|
||||
VNEWARRAY(208),
|
||||
MULTIVNEWARRAY(209),
|
||||
VRETURN(210),
|
||||
VGETFIELD(211),
|
||||
TYPED(212),
|
||||
VBOX(216),
|
||||
VUNBOX(217);
|
||||
|
||||
int code;
|
||||
|
||||
Opcode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
protected Opcode at(TypeTag type) {
|
||||
return at(type, 1);
|
||||
}
|
||||
|
||||
protected Opcode at(CondKind cond) {
|
||||
return at(cond.offset, 1);
|
||||
}
|
||||
|
||||
protected Opcode at(TypeTag type, int multiplier) {
|
||||
return at(type.offset, multiplier);
|
||||
}
|
||||
|
||||
private Opcode at(int offset, int multiplier) {
|
||||
if (offset < 0) throw new AssertionError();
|
||||
return Opcode.values()[code + (multiplier * offset)];
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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.util.function.Consumer;
|
||||
import java.util.function.ToIntBiFunction;
|
||||
|
||||
/**
|
||||
* An interface for building and tracking constant pools.
|
||||
*
|
||||
* @param <S> the type of the symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
*/
|
||||
public interface PoolHelper<S, T, E> {
|
||||
int putClass(S symbol);
|
||||
|
||||
int putFieldRef(S owner, CharSequence name, T type);
|
||||
|
||||
int putMethodRef(S owner, CharSequence name, T type, boolean isInterface);
|
||||
|
||||
int putUtf8(CharSequence s);
|
||||
|
||||
int putInt(int i);
|
||||
|
||||
int putFloat(float f);
|
||||
|
||||
int putLong(long l);
|
||||
|
||||
int putDouble(double d);
|
||||
|
||||
int putString(String s);
|
||||
|
||||
int putType(T t);
|
||||
|
||||
int putMethodType(T t);
|
||||
|
||||
int putHandle(int refKind, S owner, CharSequence name, T type);
|
||||
|
||||
int putHandle(int refKind, S owner, CharSequence name, T type, boolean isInterface);
|
||||
|
||||
int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs);
|
||||
|
||||
int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs);
|
||||
|
||||
int size();
|
||||
|
||||
E entries();
|
||||
|
||||
interface StaticArgListBuilder<S, T, E> {
|
||||
StaticArgListBuilder<S, T, E> add(int i);
|
||||
StaticArgListBuilder<S, T, E> add(float f);
|
||||
StaticArgListBuilder<S, T, E> add(long l);
|
||||
StaticArgListBuilder<S, T, E> add(double d);
|
||||
StaticArgListBuilder<S, T, E> add(String s);
|
||||
StaticArgListBuilder<S, T, E> add(int refKind, S owner, CharSequence name, T type);
|
||||
<Z> StaticArgListBuilder<S, T, E> add(Z z, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc);
|
||||
StaticArgListBuilder<S, T, E> add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgList);
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public enum PoolTag {
|
||||
CONSTANT_UTF8(1),
|
||||
CONSTANT_UNICODE(2),
|
||||
CONSTANT_INTEGER(3),
|
||||
CONSTANT_FLOAT(4),
|
||||
CONSTANT_LONG(5),
|
||||
CONSTANT_DOUBLE(6),
|
||||
CONSTANT_CLASS(7),
|
||||
CONSTANT_STRING(8),
|
||||
CONSTANT_FIELDREF(9),
|
||||
CONSTANT_METHODREF(10),
|
||||
CONSTANT_INTERFACEMETHODREF(11),
|
||||
CONSTANT_NAMEANDTYPE(12),
|
||||
CONSTANT_METHODHANDLE(15),
|
||||
CONSTANT_METHODTYPE(16),
|
||||
CONSTANT_DYNAMIC(17),
|
||||
CONSTANT_INVOKEDYNAMIC(18);
|
||||
|
||||
public final int tag;
|
||||
|
||||
PoolTag(int tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
static PoolTag from(int tag) {
|
||||
return values()[tag - 1];
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface Type {
|
||||
TypeTag getTag();
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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.util.Iterator;
|
||||
|
||||
/**
|
||||
* Helper to create and manipulate type descriptors of T.
|
||||
*
|
||||
* @param <S> the type of symbols
|
||||
* @param <T> the type of type descriptors
|
||||
*/
|
||||
public interface TypeHelper<S, T> {
|
||||
/**
|
||||
* Return the type descriptor of an element given the type
|
||||
* descriptor of an array.
|
||||
*
|
||||
* @param t the type descriptor of the array
|
||||
* @return the element type
|
||||
*/
|
||||
T elemtype(T t);
|
||||
|
||||
/**
|
||||
* Return the type descriptor of an array given the type descriptor
|
||||
* of an element.
|
||||
*
|
||||
* @param t the type descriptor of the element
|
||||
* @return the type descriptor of the array
|
||||
*/
|
||||
T arrayOf(T t);
|
||||
|
||||
/**
|
||||
* Return an iterator over the type descriptors of the parameters of a
|
||||
* method.
|
||||
*
|
||||
* @param t the method type descriptor
|
||||
* @return an iterator over the type descriptors of the parameters
|
||||
*/
|
||||
Iterator<T> parameterTypes(T t);
|
||||
|
||||
/**
|
||||
* Return the type descriptor of a {@code TypeTag}.
|
||||
*
|
||||
* @param tag the {@code TypeTag} of a primitive type
|
||||
* @return the type descriptor of the primitive type
|
||||
*/
|
||||
T fromTag(TypeTag tag);
|
||||
|
||||
/**
|
||||
* Return the return type descriptor of a method.
|
||||
*
|
||||
* @param t the method type descriptor
|
||||
* @return the return type descriptor
|
||||
*/
|
||||
T returnType(T t);
|
||||
|
||||
/**
|
||||
* Return the type descriptor for a symbol.
|
||||
*
|
||||
* @param s the symbol
|
||||
* @return the type descriptor
|
||||
*/
|
||||
T type(S s);
|
||||
|
||||
/**
|
||||
* Return the symbol corresponding to a type descriptor.
|
||||
*
|
||||
* @param type the type descriptor
|
||||
* @return the symbol
|
||||
*/
|
||||
S symbol(T type);
|
||||
|
||||
/**
|
||||
* Return the {@code TypeTag} corresponding to a type descriptor. Reference
|
||||
* types return {@code TypeTag.A}.
|
||||
*
|
||||
* @param t a type descriptor
|
||||
* @return the corresponding {@code TypeTag}
|
||||
*/
|
||||
TypeTag tag(T t);
|
||||
|
||||
/**
|
||||
* Return the symbol corresponding to a JVM type descriptor string.
|
||||
*
|
||||
* @param s a JVM type descriptor string
|
||||
* @return the corresponding symbol
|
||||
*/
|
||||
S symbolFrom(String s);
|
||||
|
||||
/**
|
||||
* Return the common supertype descriptor of two type descriptors.
|
||||
*
|
||||
* @param t1 a type descriptor
|
||||
* @param t2 a type descriptor
|
||||
* @return the common supertype descriptor
|
||||
*/
|
||||
T commonSupertype(T t1, T t2);
|
||||
|
||||
/**
|
||||
* Return the type descriptor for the null type.
|
||||
*
|
||||
* @return the type descriptor for the null type
|
||||
*/
|
||||
T nullType();
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public enum TypeTag implements Type {
|
||||
/**
|
||||
* byte
|
||||
*/
|
||||
B("B", 0, 1, 8),
|
||||
/**
|
||||
* short
|
||||
*/
|
||||
S("S", 0, 1, 9),
|
||||
/**
|
||||
* int
|
||||
*/
|
||||
I("I", 0, 1, 10),
|
||||
/**
|
||||
* float
|
||||
*/
|
||||
F("F", 2, 1, 6),
|
||||
/**
|
||||
* long
|
||||
*/
|
||||
J("J", 1, 2, 11),
|
||||
/**
|
||||
* double
|
||||
*/
|
||||
D("D", 3, 2, 7),
|
||||
/**
|
||||
* Reference type
|
||||
*/
|
||||
A("A", 4, 1, -1),
|
||||
/**
|
||||
* char
|
||||
*/
|
||||
C("C", 0, 1, 5),
|
||||
/**
|
||||
* boolean
|
||||
*/
|
||||
Z("Z", 0, 1, 4),
|
||||
/**
|
||||
* void
|
||||
*/
|
||||
V("V", -1, -1, -1),
|
||||
/**
|
||||
* Value type
|
||||
*/
|
||||
Q("Q", -1, 1, -1);
|
||||
|
||||
String typeStr;
|
||||
int offset;
|
||||
int width;
|
||||
int newarraycode;
|
||||
|
||||
TypeTag(String typeStr, int offset, int width, int newarraycode) {
|
||||
this.typeStr = typeStr;
|
||||
this.offset = offset;
|
||||
this.width = width;
|
||||
this.newarraycode = newarraycode;
|
||||
}
|
||||
|
||||
static TypeTag commonSupertype(TypeTag t1, TypeTag t2) {
|
||||
if (t1.isIntegral() && t2.isIntegral()) {
|
||||
int p1 = t1.ordinal();
|
||||
int p2 = t2.ordinal();
|
||||
return (p1 <= p2) ? t2 : t1;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int width() {
|
||||
return width;
|
||||
}
|
||||
|
||||
boolean isIntegral() {
|
||||
switch (this) {
|
||||
case B:
|
||||
case S:
|
||||
case I:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeTag getTag() {
|
||||
return this;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user