diff options
Diffstat (limited to 'src')
22 files changed, 413 insertions, 85 deletions
diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 24f4e25f..3b73f8ac 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -69,7 +69,7 @@ public abstract class CtClass { /** * The version number of this release. */ - public static final String version = "3.29.2-GA"; + public static final String version = "3.30.2-GA"; private int linesCount = 0; @@ -82,7 +82,7 @@ public abstract class CtClass { */ public static void main(String[] args) { System.out.println("Javassist version " + CtClass.version); - System.out.println("Copyright (C) 1999-2022 Shigeru Chiba." + System.out.println("Copyright (C) 1999-2023 Shigeru Chiba." + " All Rights Reserved."); } diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 22b873a6..f4345e0a 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -1763,8 +1763,7 @@ class CtClassType extends CtClass { int pos = it.insertEx(initializer.get()); it.insert(initializer.getExceptionTable(), pos); int maxstack = codeAttr.getMaxStack(); - if (maxstack < stacksize) - codeAttr.setMaxStack(stacksize); + codeAttr.setMaxStack(maxstack + stacksize); } private int makeFieldInitializer(Bytecode code, CtClass[] parameters) diff --git a/src/main/javassist/bytecode/BootstrapMethodsAttribute.java b/src/main/javassist/bytecode/BootstrapMethodsAttribute.java index 94a0481f..0fd04cf8 100644 --- a/src/main/javassist/bytecode/BootstrapMethodsAttribute.java +++ b/src/main/javassist/bytecode/BootstrapMethodsAttribute.java @@ -2,6 +2,7 @@ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; +import java.util.Arrays; import java.util.Map; public class BootstrapMethodsAttribute extends AttributeInfo { @@ -35,6 +36,26 @@ public class BootstrapMethodsAttribute extends AttributeInfo { * <code>bootstrap_arguments</code>. */ public int[] arguments; + + /** + * Makes a copy. Class names are replaced according to the + * * given <code>Map</code> object. + * + * @param srcCp the constant pool table from the source + * @param destCp the constant pool table used bt new copy + * @param classnames pairs of replaced and substituted class names. + * + * @return new BootstrapMethod + */ + protected BootstrapMethod copy(ConstPool srcCp, ConstPool destCp, Map<String,String> classnames) { + int newMethodRef = srcCp.copy(methodRef, destCp, classnames); + int[] newArguments = new int[arguments.length]; + + for (int i = 0; i < arguments.length; i++) + newArguments[i] = srcCp.copy(arguments[i], destCp, classnames); + + return new BootstrapMethod(newMethodRef, newArguments); + } } BootstrapMethodsAttribute(ConstPool cp, int n, DataInputStream in) @@ -51,25 +72,8 @@ public class BootstrapMethodsAttribute extends AttributeInfo { */ public BootstrapMethodsAttribute(ConstPool cp, BootstrapMethod[] methods) { super(cp, tag); - int size = 2; - for (int i = 0; i < methods.length; i++) - size += 4 + methods[i].arguments.length * 2; - - byte[] data = new byte[size]; - ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods - int pos = 2; - for (int i = 0; i < methods.length; i++) { - ByteArray.write16bit(methods[i].methodRef, data, pos); - ByteArray.write16bit(methods[i].arguments.length, data, pos + 2); - int[] args = methods[i].arguments; - pos += 4; - for (int k = 0; k < args.length; k++) { - ByteArray.write16bit(args[k], data, pos); - pos += 2; - } - } - set(data); + set(convertMethodsToBytes(methods)); } /** @@ -113,12 +117,67 @@ public class BootstrapMethodsAttribute extends AttributeInfo { BootstrapMethod[] methods = getMethods(); ConstPool thisCp = getConstPool(); for (int i = 0; i < methods.length; i++) { - BootstrapMethod m = methods[i]; - m.methodRef = thisCp.copy(m.methodRef, newCp, classnames); - for (int k = 0; k < m.arguments.length; k++) - m.arguments[k] = thisCp.copy(m.arguments[k], newCp, classnames); + methods[i] = methods[i].copy(thisCp, newCp, classnames); } return new BootstrapMethodsAttribute(newCp, methods); } + + /** + * add bootstrap method from given <code>ConstPool</code> and <code>BootstrapMethod</code>, + * and add it to the specified index. Class names are replaced according to the + * given <code>Map</code> object. + * + * <p> + * if the index less than 0 or large than the origin method length, then throw <code>RuntimeException</code>;<br> + * if the index large or equals to 0 and less or equals to the origin method length, + * then replace the origin method with the new <code>BootstrapMethod srcBm</code> ;<br> + * if the index equals to the origin method length, then append the new <code>BootstrapMethod srcBm</code> at + * the origin methods tail. + * </p> + * + * @param srcCp the constant pool table of source. + * @param srcBm the bootstrap method of source + * @param index the new method index on bootstrap methods + * @param classnames pairs of replaced and substituted + * class names. + */ + public void addMethod(ConstPool srcCp, BootstrapMethod srcBm, int index, Map<String,String> classnames) { + BootstrapMethod[] methods = getMethods(); + + if (index < 0 || index > methods.length) { + throw new RuntimeException("index out of range"); + } + + if (index == methods.length) { + BootstrapMethod[] newBmArray = new BootstrapMethod[methods.length + 1]; + System.arraycopy(methods, 0, newBmArray, 0, methods.length); + methods = newBmArray; + } + + methods[index] = srcBm.copy(srcCp, getConstPool(), classnames); + set(convertMethodsToBytes(methods)); + } + + private static byte[] convertMethodsToBytes(BootstrapMethod[] methods) { + int size = 2; + for (int i = 0; i < methods.length; i++) + size += 4 + methods[i].arguments.length * 2; + + byte[] data = new byte[size]; + ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods + int pos = 2; + for (int i = 0; i < methods.length; i++) { + ByteArray.write16bit(methods[i].methodRef, data, pos); + ByteArray.write16bit(methods[i].arguments.length, data, pos + 2); + int[] args = methods[i].arguments; + pos += 4; + for (int k = 0; k < args.length; k++) { + ByteArray.write16bit(args[k], data, pos); + pos += 2; + } + } + + return data; + } } diff --git a/src/main/javassist/bytecode/CodeAttribute.java b/src/main/javassist/bytecode/CodeAttribute.java index 98479b21..cf8806ef 100644 --- a/src/main/javassist/bytecode/CodeAttribute.java +++ b/src/main/javassist/bytecode/CodeAttribute.java @@ -16,6 +16,8 @@ package javassist.bytecode; +import javassist.*; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -76,8 +78,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode { * class names. */ private CodeAttribute(ConstPool cp, CodeAttribute src, Map<String,String> classnames) - throws BadBytecode - { + throws BadBytecode, NotFoundException, CannotCompileException { super(cp, tag); maxStack = src.getMaxStack(); @@ -139,6 +140,10 @@ public class CodeAttribute extends AttributeInfo implements Opcode { } catch (BadBytecode e) { throw new RuntimeCopyException("bad bytecode. fatal?"); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } catch (CannotCompileException e) { + throw new RuntimeException(e); } } @@ -324,7 +329,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode { * * @param smt the stack map table added to this code attribute. * If it is null, a new stack map is not added. - * Only the old stack map is removed. + * Only the old stack map is removed. */ public void setAttribute(StackMapTable smt) { AttributeInfo.remove(attributes, StackMapTable.tag); @@ -366,11 +371,11 @@ public class CodeAttribute extends AttributeInfo implements Opcode { */ private byte[] copyCode(ConstPool destCp, Map<String,String> classnames, ExceptionTable etable, CodeAttribute destCa) - throws BadBytecode - { + throws BadBytecode, NotFoundException, CannotCompileException { int len = getCodeLength(); byte[] newCode = new byte[len]; destCa.info = newCode; + LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(), newCode, destCp, classnames); return LdcEntry.doit(newCode, ldc, etable, destCa); @@ -378,9 +383,8 @@ public class CodeAttribute extends AttributeInfo implements Opcode { private static LdcEntry copyCode(byte[] code, int beginPos, int endPos, ConstPool srcCp, byte[] newcode, - ConstPool destCp, Map<String,String> classnameMap) - throws BadBytecode - { + ConstPool destCp, Map<String,String> classnameMap) + throws BadBytecode, NotFoundException, CannotCompileException { int i2, index; LdcEntry ldcEntry = null; @@ -429,6 +433,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode { case INVOKEDYNAMIC : copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); + copyBootstrapMethod(srcCp, destCp, i + 1, code, newcode, classnameMap); newcode[i + 3] = 0; newcode[i + 4] = 0; break; @@ -448,6 +453,134 @@ public class CodeAttribute extends AttributeInfo implements Opcode { return ldcEntry; } + /** + * Copy the Bootstrap method of the specified index referenced in the source <code>InvokeDynamic</code> directive + * to the specified index in the destination Boostrap Attribute.<br> + * if the Bootstrap Attribute does not exist in the destination class, create a new Bootstrap Attribute; <br> + * if the destination Bootstrap Method already exists at the specified index method, + * the method at that position will be overwritten, otherwise it will be added + * at the end of the destination Bootstrap method. + * + * @param srcCp the constant pool table of source + * @param destCp the constant pool table of destination + * @param codeIndex the index of the invoke dynamic first parameter in code array + * @param srcCode the code array of source + * @param newCode the code array of destination + * @param classnameMap pairs of replaced and substituted class names. + * + * @throws NotFoundException this exception thrown when the class + * cannot be found in the default <code>ClassPool</code> + * @throws CannotCompileException this exception thrown from the method + * {@link #copyInvokeStaticMethod(CtClass, ConstPool, + * BootstrapMethodsAttribute.BootstrapMethod, CtClass, Map)} + */ + private static void copyBootstrapMethod(ConstPool srcCp, ConstPool destCp, int codeIndex, byte[] srcCode, + byte[] newCode, Map<String,String> classnameMap) + throws NotFoundException, CannotCompileException { + ClassPool classPool = ClassPool.getDefault(); + CtClass srcCc = classPool.get(srcCp.getClassName()); + CtClass destCc = classPool.get(destCp.getClassName()); + ClassFile srcCf = srcCc.getClassFile(); + ClassFile destCf = destCc.getClassFile(); + BootstrapMethodsAttribute srcBma = (BootstrapMethodsAttribute) + srcCf.getAttribute(BootstrapMethodsAttribute.tag); + + // if source class does not have bootstrap attribute then stop copy + if (srcBma == null) { + return; + } + + BootstrapMethodsAttribute destBma = (BootstrapMethodsAttribute) + destCf.getAttribute(BootstrapMethodsAttribute.tag); + + int srcCpIndex = ((srcCode[codeIndex] & 0xff) << 8) | (srcCode[codeIndex + 1] & 0xff); + int destCpIndex = ((newCode[codeIndex] & 0xff) << 8) | (newCode[codeIndex + 1] & 0xff); + int srcBmIndex = srcCp.getInvokeDynamicBootstrap(srcCpIndex); + int destBmIndex = destCp.getInvokeDynamicBootstrap(destCpIndex); + + // if source class does not have bootstrap attribute, then create bootstrap attribute + if (destBma == null) { + destBma = new BootstrapMethodsAttribute(destCp, + new BootstrapMethodsAttribute.BootstrapMethod[0]); + destCf.addAttribute(destBma); + } + + BootstrapMethodsAttribute.BootstrapMethod srcBm = srcBma.getMethods()[srcBmIndex]; + destBma.addMethod(srcCp, srcBm, destBmIndex, classnameMap); + + copyInvokeStaticMethod(srcCc, srcCp, srcBm, destCc, classnameMap); + } + + /** + * Copy the static methods referenced by the bootstrap method in this class (such as some lambda methods).<br> + * If the source method exists in the destination class, it will be ignored. + * + * @param srcCc source class + * @param srcCp constant pool table of source class + * @param srcBm source method to be copied + * @param destCc destination class + * @param classnameMap irs of replaced and substituted class names. + * + * @throws CannotCompileException thrown by {@link CtNewMethod#copy(CtMethod, CtClass, ClassMap)} + * or{@link CtClass#addMethod(CtMethod)} + */ + private static void copyInvokeStaticMethod(CtClass srcCc, ConstPool srcCp, + BootstrapMethodsAttribute.BootstrapMethod srcBm, CtClass destCc, + Map<String, String> classnameMap) throws CannotCompileException { + for (int argument : srcBm.arguments) { + ConstInfo constInfo = srcCp.getItem(argument); + + if (!(constInfo instanceof MethodHandleInfo)) continue; + + MethodHandleInfo methodHandleInfo = (MethodHandleInfo) constInfo; + if (ConstPool.REF_invokeStatic != methodHandleInfo.refKind) continue; + + String methodRefClassName = srcCp.getMethodrefClassName(methodHandleInfo.refIndex); + if (methodRefClassName == null || !methodRefClassName.equals(srcCc.getName())) continue; + + String staticMethodName = srcCp.getMethodrefName(methodHandleInfo.refIndex); + String staticMethodSignature = srcCp.getMethodrefType(methodHandleInfo.refIndex); + CtMethod srcMethod = getStaticCtMethod(srcCc, staticMethodName, staticMethodSignature); + + if (!checkStaticMethodExisted(destCc, staticMethodName, staticMethodSignature)) { + ClassMap classMap = new ClassMap(); + classMap.putAll(classnameMap); + + CtMethod ctMethod = CtNewMethod.copy(srcMethod, destCc, classMap); + destCc.addMethod(ctMethod); + } + } + } + + private static CtMethod getStaticCtMethod(CtClass ctClass, String staticMethodName, String staticMethodSignature) { + CtMethod srcMethod = null; + for (CtMethod declaredMethod : ctClass.getDeclaredMethods()) { + if (Modifier.isStatic(declaredMethod.getModifiers()) + && declaredMethod.getName().equals(staticMethodName) + && declaredMethod.getSignature().equals(staticMethodSignature)) { + srcMethod = declaredMethod; + break; + } + } + + if (srcMethod == null) { + throw new RuntimeException("Can not found static method:" + staticMethodName); + } + return srcMethod; + } + + private static boolean checkStaticMethodExisted(CtClass ctClass, String staticMethodName, String staticMethodSignature) { + for (CtMethod declaredMethod : ctClass.getDeclaredMethods()) { + if (Modifier.isStatic(declaredMethod.getModifiers()) + && declaredMethod.getName().equals(staticMethodName) + && declaredMethod.getSignature().equals(staticMethodSignature)) { + return true; + } + } + + return false; + } + private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp, byte[] newcode, ConstPool destCp, Map<String,String> classnameMap) { diff --git a/src/main/javassist/bytecode/MethodParametersAttribute.java b/src/main/javassist/bytecode/MethodParametersAttribute.java index b9c252a9..f78f6c72 100644 --- a/src/main/javassist/bytecode/MethodParametersAttribute.java +++ b/src/main/javassist/bytecode/MethodParametersAttribute.java @@ -32,7 +32,8 @@ public class MethodParametersAttribute extends AttributeInfo { byte[] data = new byte[names.length * 4 + 1]; data[0] = (byte)names.length; for (int i = 0; i < names.length; i++) { - ByteArray.write16bit(cp.addUtf8Info(names[i]), data, i * 4 + 1); + String name = names[i]; + ByteArray.write16bit(name == null ? 0 : cp.addUtf8Info(name), data, i * 4 + 1); ByteArray.write16bit(flags[i], data, i * 4 + 3); } @@ -61,7 +62,8 @@ public class MethodParametersAttribute extends AttributeInfo { * @param i the position of the parameter. */ public String parameterName(int i) { - return getConstPool().getUtf8Info(name(i)); + int index = name(i); + return index == 0 ? null : getConstPool().getUtf8Info(index); } /** @@ -87,7 +89,8 @@ public class MethodParametersAttribute extends AttributeInfo { String[] names = new String[s]; int[] flags = new int[s]; for (int i = 0; i < s; i++) { - names[i] = cp.getUtf8Info(name(i)); + int index = name(i); + names[i] = index == 0 ? null : cp.getUtf8Info(index); flags[i] = accessFlags(i); } diff --git a/src/main/javassist/compiler/MemberResolver.java b/src/main/javassist/compiler/MemberResolver.java index 2ccb0ff7..3b0cc012 100644 --- a/src/main/javassist/compiler/MemberResolver.java +++ b/src/main/javassist/compiler/MemberResolver.java @@ -130,9 +130,7 @@ public class MemberResolver implements TokenId { if (onlyExact) maybe = null; - else - if (maybe != null) - return maybe; + //else maybe super class has more precise match int mod = clazz.getModifiers(); boolean isIntf = Modifier.isInterface(mod); @@ -143,8 +141,11 @@ public class MemberResolver implements TokenId { if (pclazz != null) { Method r = lookupMethod(pclazz, methodName, argTypes, argDims, argClassNames, onlyExact); - if (r != null) - return r; + if (r != null) { + if (maybe == null || maybe.notmatch > r.notmatch) { + maybe = r; + } + } } } } @@ -156,8 +157,11 @@ public class MemberResolver implements TokenId { Method r = lookupMethod(intf, methodName, argTypes, argDims, argClassNames, onlyExact); - if (r != null) - return r; + if (r != null) { + if (maybe == null || maybe.notmatch > r.notmatch) { + maybe = r; + } + } } if (isIntf) { @@ -166,8 +170,11 @@ public class MemberResolver implements TokenId { if (pclazz != null) { Method r = lookupMethod(pclazz, methodName, argTypes, argDims, argClassNames, onlyExact); - if (r != null) - return r; + if (r != null) { + if (maybe == null || maybe.notmatch > r.notmatch) { + maybe = r; + } + } } } } diff --git a/src/main/javassist/convert/TransformNewClass.java b/src/main/javassist/convert/TransformNewClass.java index 83f2a0cc..56738a10 100644 --- a/src/main/javassist/convert/TransformNewClass.java +++ b/src/main/javassist/convert/TransformNewClass.java @@ -56,9 +56,6 @@ final public class TransformNewClass extends Transformer { if (c == NEW) { index = iterator.u16bitAt(pos + 1); if (cp.getClassInfo(index).equals(classname)) { - if (iterator.byteAt(pos + 3) != DUP) - throw new CannotCompileException( - "NEW followed by no DUP was found"); if (newClassIndex == 0) newClassIndex = cp.addClassInfo(newClassName); diff --git a/src/main/javassist/expr/NewExpr.java b/src/main/javassist/expr/NewExpr.java index 863f4db5..e45a4c54 100644 --- a/src/main/javassist/expr/NewExpr.java +++ b/src/main/javassist/expr/NewExpr.java @@ -193,6 +193,14 @@ public class NewExpr extends Expr { */ int codeSize = canReplace(); int end = pos + codeSize; + //check isStoreBeforeInit ,such as : new xx/xx ; dup;[astoreN];invokespecial xx/xx; + int beforeStoreOp = 0; + int preOp = iterator.byteAt(currentPos - 1); + if (iterator.byteAt(newPos + 3) == Opcode.DUP + && (preOp >= Opcode.ASTORE_0 + && preOp <= Opcode.ASTORE_3) && currentPos - newPos == 5) { + beforeStoreOp = preOp; + } for (int i = pos; i < end; ++i) iterator.writeByte(NOP, i); @@ -230,7 +238,12 @@ public class NewExpr extends Expr { if (codeSize > 3) // if the original code includes DUP. bytecode.addAload(retVar); - replace0(pos, bytecode, bytecodeSize); + if (beforeStoreOp >= Opcode.ASTORE_0) { + bytecode.addOpcode(beforeStoreOp); + replace0(pos - 1, bytecode, bytecodeSize + 1); + } else { + replace0(pos, bytecode, bytecodeSize); + } } catch (CompileError e) { throw new CannotCompileException(e); } catch (NotFoundException e) { throw new CannotCompileException(e); } diff --git a/src/test/javassist/Bench.java b/src/test/javassist/Bench.java index b7b8b27e..1892b383 100644 --- a/src/test/javassist/Bench.java +++ b/src/test/javassist/Bench.java @@ -5,9 +5,6 @@ import javassist.expr.*; import javassist.compiler.*; public class Bench extends JvstTestRoot { - public Bench(String name) { - super(name); - } public void testProceed() throws Exception { CtClass cc = sloader.get("test.BenchProceed"); diff --git a/src/test/javassist/JvstTest.java b/src/test/javassist/JvstTest.java index 3ed16827..ecbf91f7 100644 --- a/src/test/javassist/JvstTest.java +++ b/src/test/javassist/JvstTest.java @@ -20,9 +20,6 @@ public class JvstTest extends JvstTestRoot { java9 = javassist.bytecode.ClassFile.MAJOR_VERSION >= javassist.bytecode.ClassFile.JAVA_9; } - public JvstTest(String name) { - super(name); - } public void testConfig() { // is the value of PATH correct? diff --git a/src/test/javassist/JvstTest2.java b/src/test/javassist/JvstTest2.java index 5d01961d..334282d5 100644 --- a/src/test/javassist/JvstTest2.java +++ b/src/test/javassist/JvstTest2.java @@ -14,9 +14,6 @@ import test2.DefineClassCapability; @SuppressWarnings({"rawtypes","unused"}) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class JvstTest2 extends JvstTestRoot { - public JvstTest2(String name) { - super(name); - } public void testInsertAt() throws Exception { CtClass cc = sloader.get("test2.InsertAt"); diff --git a/src/test/javassist/JvstTest3.java b/src/test/javassist/JvstTest3.java index c065170c..2f9ffcd5 100644 --- a/src/test/javassist/JvstTest3.java +++ b/src/test/javassist/JvstTest3.java @@ -7,9 +7,6 @@ import test3.*; @SuppressWarnings({"rawtypes","unchecked","unused"}) public class JvstTest3 extends JvstTestRoot { - public JvstTest3(String name) { - super(name); - } public void testAnnotation() throws Exception { CtClass cc = sloader.get("test3.AnnoTest"); diff --git a/src/test/javassist/JvstTest4.java b/src/test/javassist/JvstTest4.java index 259451b9..e00c332a 100644 --- a/src/test/javassist/JvstTest4.java +++ b/src/test/javassist/JvstTest4.java @@ -16,9 +16,6 @@ import javassist.expr.*; @SuppressWarnings({"rawtypes","unchecked","unused"}) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class JvstTest4 extends JvstTestRoot { - public JvstTest4(String name) { - super(name); - } public void testInsertLocalVars() throws Exception { CtClass cc = sloader.get("test4.LocalVars"); diff --git a/src/test/javassist/JvstTest5.java b/src/test/javassist/JvstTest5.java index 4a370632..dce27ab1 100644 --- a/src/test/javassist/JvstTest5.java +++ b/src/test/javassist/JvstTest5.java @@ -1,6 +1,10 @@ package javassist; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; import java.lang.reflect.TypeVariable; import javassist.bytecode.AccessFlag; @@ -9,6 +13,8 @@ import javassist.bytecode.AttributeInfo; import javassist.bytecode.ClassFile; import javassist.bytecode.ConstPool; import javassist.bytecode.InnerClassesAttribute; +import javassist.bytecode.MethodInfo; +import javassist.bytecode.MethodParametersAttribute; import javassist.bytecode.NestHostAttribute; import javassist.bytecode.NestMembersAttribute; import javassist.expr.ExprEditor; @@ -19,9 +25,6 @@ import junit.framework.Assert; @SuppressWarnings({"rawtypes","unchecked","unused"}) public class JvstTest5 extends JvstTestRoot { - public JvstTest5(String name) { - super(name); - } public void testDollarClassInStaticMethod() throws Exception { CtClass cc = sloader.makeClass("test5.DollarClass"); @@ -161,7 +164,7 @@ public class JvstTest5 extends JvstTestRoot { CtClass cc = sloader.makeClass("test5.JIRA256"); ClassFile ccFile = cc.getClassFile(); ConstPool constpool = ccFile.getConstPool(); - + AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag); javassist.bytecode.annotation.Annotation entityAnno = new javassist.bytecode.annotation.Annotation("test5.Entity", constpool); @@ -176,7 +179,7 @@ public class JvstTest5 extends JvstTestRoot { assertTrue(o.getClass().getName().equals("test5.JIRA256")); java.lang.annotation.Annotation[] annotations = o.getClass().getDeclaredAnnotations(); - assertEquals(1, annotations.length); + assertEquals(1, annotations.length); } public void testJIRA250() throws Exception { @@ -593,4 +596,51 @@ public class JvstTest5 extends JvstTestRoot { } catch (CannotCompileException e) {} } + + public void testGithubIssue462Java21WithoutParameters() throws IOException { + + //This is a class file compiled by Java-21 + //javac Java21InnerClassWithoutParameters.java + //public class Java21InnerClassWithoutParameters { + // class InnerClass implements Runnable { + // public void run() { + // } + // } + //} + String classFileName = "./Java21InnerClassWithoutParameters$InnerClass.class"; + ClassLoader classLoader = getClass().getClassLoader(); + File classFile = new File(classLoader.getResource(classFileName).getFile()); + + CtClass cc = sloader.makeClass(new FileInputStream(classFile)); + cc.getClassFile().compact(); + + ClassFile cf = cc.getClassFile2(); + ConstPool cp = cf.getConstPool(); + + MethodInfo minfo = cf.getMethod("<init>"); + MethodParametersAttribute attr + = (MethodParametersAttribute)minfo.getAttribute(MethodParametersAttribute.tag); + assertEquals(1, attr.size()); + assertNull(attr.parameterName(0)); + } + + public void testSuperCall() throws Exception { + String javacResult = new BearKeeper().javacResult(); + assertEquals("Man feed(Bear)", javacResult); + + CtClass cc = sloader.get("javassist.BearKeeper"); + CtMethod cm = CtMethod.make( + "public String javassistResult() {return super.feed(new javassist.Bear());}", + cc); + cc.addMethod(cm); + cc.setModifiers(Modifier.PUBLIC); + cc.writeFile(); + Object obj = make(cc.getName()); + Method m = obj.getClass().getMethod("javassistResult"); + Object javassistResult = m.invoke(obj); + + //before this fix + //expected:<Man feed(Bear)> but was:<Keeper feed(Animal)> + assertEquals(javacResult, javassistResult); + } } diff --git a/src/test/javassist/JvstTestRoot.java b/src/test/javassist/JvstTestRoot.java index 69f4a6ae..923af666 100644 --- a/src/test/javassist/JvstTestRoot.java +++ b/src/test/javassist/JvstTestRoot.java @@ -13,10 +13,6 @@ public class JvstTestRoot extends TestCase { ClassPool sloader, dloader; Loader cloader; - public JvstTestRoot(String name) { - super(name); - } - protected void print(String msg) { System.out.println(msg); } diff --git a/src/test/javassist/SuperCallCase.java b/src/test/javassist/SuperCallCase.java new file mode 100644 index 00000000..8d5ea9e7 --- /dev/null +++ b/src/test/javassist/SuperCallCase.java @@ -0,0 +1,38 @@ +package javassist; + +class Animal { +} + +class Bear extends Animal { +} + + +/** + * Base class has a method with precise type. + */ +class Man { + String feed(Bear bear) { + return "Man feed(Bear)"; + } +} + +/** + * Derived class has a method which has same name with base class's and more imprecise type. + */ +class Keeper extends Man { + String feed(Animal animal) { + return "Keeper feed(Animal)"; + } +} + +/** + * Derived class has a method which call super method with precise type. + */ +class BearKeeper extends Keeper { + public BearKeeper() { + } + + String javacResult() { + return super.feed(new Bear()); + } +} diff --git a/src/test/javassist/bytecode/BytecodeTest.java b/src/test/javassist/bytecode/BytecodeTest.java index eac420bc..68e3b2c2 100644 --- a/src/test/javassist/bytecode/BytecodeTest.java +++ b/src/test/javassist/bytecode/BytecodeTest.java @@ -6,6 +6,7 @@ import junit.framework.*; import javassist.*; import javassist.bytecode.annotation.*; import javassist.bytecode.SignatureAttribute.*; +import test4.InvokeDynCopyDest; @SuppressWarnings("unused") public class BytecodeTest extends TestCase { @@ -461,19 +462,19 @@ public class BytecodeTest extends TestCase { public void testSignatureChange() throws Exception { changeMsig("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Object", "java/lang/Object", - "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object", "java/lang/Objec"); + "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object", "java/lang/Objec"); changeMsig("<S:Ljava/lang/Object;>(TS;[TS;)TT;", "java/lang/Object", - "<S:Ljava/lang/Objec;>(TS;[TS;)TT;", "java/lang/Objec"); + "<S:Ljava/lang/Objec;>(TS;[TS;)TT;", "java/lang/Objec"); changeMsig("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Object", - "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Objec"); + "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Objec"); changeMsig("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object", - "<S:Ljava/lang/Object2;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object2"); + "<S:Ljava/lang/Object2;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object2"); changeMsig2("<S:Ljava/lang/Object;>(TS;[TS;)TT;", "java/lang/Object", - "<S:Ljava/lang/Objec;>(TS;[TS;)TT;", "java/lang/Objec"); + "<S:Ljava/lang/Objec;>(TS;[TS;)TT;", "java/lang/Objec"); changeMsig2("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Object", - "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Objec"); + "<S:Ljava/lang/Objec;>(TS;[TS;)Ljava/lang/Object2;", "java/lang/Objec"); changeMsig2("<S:Ljava/lang/Object;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object", - "<S:Ljava/lang/Object2;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object2"); + "<S:Ljava/lang/Object2;>(TS;[TS;)Ljava/lang/Objec;", "java/lang/Object2"); String sig = "<T:Ljava/lang/Exception;>LPoi$Foo<Ljava/lang/String;>;LBar;LBar2;"; String res = "<T:Ljava/lang/Exception;>LPoi$Foo<Ljava/lang/String2;>;LBar;LBar2;"; changeMsig(sig, "java/lang/String", res, "java/lang/String2"); @@ -683,7 +684,7 @@ public class BytecodeTest extends TestCase { assertFalse(fi1.equals(fi3)); assertFalse(fi1.equals(ci1)); assertFalse(fi1.equals(null)); - + LongInfo li1 = new LongInfo(12345L, n++); LongInfo li2 = new LongInfo(12345L, n++); LongInfo li3 = new LongInfo(-12345L, n++); @@ -834,6 +835,28 @@ public class BytecodeTest extends TestCase { assertEquals("(I)V", cPool2.getUtf8Info(cPool2.getMethodTypeInfo(mtIndex))); } + public void testInvokeDynamicWithCopy() throws Exception { + CtClass srcCc = loader.get("test4.InvokeDynCopySrc"); + CtClass destCc = loader.get("test4.InvokeDynCopyDest"); + + // copy source constructor to dest + for (CtConstructor constructor : destCc.getConstructors()) { + for (CtConstructor srcClassConstructor : srcCc.getConstructors()) { + if (constructor.getSignature().equalsIgnoreCase(srcClassConstructor.getSignature())) { + constructor.setBody(srcClassConstructor, null); + } + } + } + + // set dest class method body by source class + destCc.getDeclaredMethod("getString").setBody(srcCc.getDeclaredMethod("getString"), new ClassMap()); + + Object destObj = (new Loader(loader)).loadClass(destCc.getName()).getConstructor().newInstance(); + + // if don't copy bootstrap method and static lambda method it will throw exception when invoke + assertEquals("hello", destObj.getClass().getMethod("getString").invoke(destObj)); + } + public static Test suite() { TestSuite suite = new TestSuite("Bytecode Tests"); suite.addTestSuite(BytecodeTest.class); diff --git a/src/test/javassist/bytecode/InsertGap0.java b/src/test/javassist/bytecode/InsertGap0.java index 425f12d7..9e94e593 100644 --- a/src/test/javassist/bytecode/InsertGap0.java +++ b/src/test/javassist/bytecode/InsertGap0.java @@ -159,9 +159,6 @@ final class Gap0Example2 { @SuppressWarnings({"rawtypes","unchecked","unused"}) public final class InsertGap0 extends JvstTestRoot { - public InsertGap0(String name) { - super(name); - } public void testExample() throws Throwable { ClassPool pool = ClassPool.getDefault(); diff --git a/src/test/resources/Java21InnerClassWithoutParameters$InnerClass.class b/src/test/resources/Java21InnerClassWithoutParameters$InnerClass.class Binary files differnew file mode 100644 index 00000000..c1c6cab5 --- /dev/null +++ b/src/test/resources/Java21InnerClassWithoutParameters$InnerClass.class diff --git a/src/test/resources/Java21InnerClassWithoutParameters.class b/src/test/resources/Java21InnerClassWithoutParameters.class Binary files differnew file mode 100644 index 00000000..a6208c52 --- /dev/null +++ b/src/test/resources/Java21InnerClassWithoutParameters.class diff --git a/src/test/test4/InvokeDynCopyDest.java b/src/test/test4/InvokeDynCopyDest.java new file mode 100644 index 00000000..003c7c53 --- /dev/null +++ b/src/test/test4/InvokeDynCopyDest.java @@ -0,0 +1,11 @@ +package test4; + +public class InvokeDynCopyDest { + public InvokeDynCopyDest() { + System.out.println("my output:" + getString()); + } + + public String getString() { + return "dest"; + } +} diff --git a/src/test/test4/InvokeDynCopySrc.java b/src/test/test4/InvokeDynCopySrc.java new file mode 100644 index 00000000..83291f7b --- /dev/null +++ b/src/test/test4/InvokeDynCopySrc.java @@ -0,0 +1,17 @@ +package test4; + +import java.util.function.Supplier; + +public class InvokeDynCopySrc { + public InvokeDynCopySrc() { + System.out.println("source class:" + getString()); + } + + public String getString() { + Supplier<String> stringSupplier = () -> { + return "hello"; + }; + + return stringSupplier.get(); + } +} |