From e71398c6fc652b8c522da858906a4385cf102130 Mon Sep 17 00:00:00 2001 From: Sam Ma Date: Wed, 20 Mar 2019 13:08:43 +1100 Subject: [PATCH] Fix #252 make instrumentation works on JDK11 for the inner class which has access to the private constructor of the host class --- .../javassist/compiler/MemberCodeGen.java | 18 ++++++++++++++-- src/test/javassist/JvstTest5.java | 21 +++++++++++++++++++ src/test/test5/NestHost3.java | 19 +++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/test/test5/NestHost3.java diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index ad254816..25be0ee1 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -636,8 +636,22 @@ public class MemberCodeGen extends CodeGen { throw new CompileError("no such constructor: " + targetClass.getName()); if (declClass != thisClass && AccessFlag.isPrivate(acc)) { - desc = getAccessibleConstructor(desc, declClass, minfo); - bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter + boolean isNested = false; + if (declClass.getClassFile().getMajorVersion() >= ClassFile.JAVA_11) { + try { + CtClass[] nestedClasses = declClass.getNestedClasses(); + for (int i = 0; i < nestedClasses.length; i++) { + if (thisClass == nestedClasses[i]) { + isNested = true; + break; + } + } + } catch (NotFoundException ignored) { } + } + if (!isNested) { + desc = getAccessibleConstructor(desc, declClass, minfo); + bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter + } } } else if (AccessFlag.isPrivate(acc)) diff --git a/src/test/javassist/JvstTest5.java b/src/test/javassist/JvstTest5.java index 915e1e1a..96f46356 100644 --- a/src/test/javassist/JvstTest5.java +++ b/src/test/javassist/JvstTest5.java @@ -14,6 +14,7 @@ import javassist.bytecode.NestMembersAttribute; import javassist.expr.ExprEditor; import javassist.expr.Handler; import javassist.expr.MethodCall; +import javassist.expr.NewExpr; @SuppressWarnings({"rawtypes","unchecked","unused"}) public class JvstTest5 extends JvstTestRoot { @@ -469,6 +470,26 @@ public class JvstTest5 extends JvstTestRoot { assertEquals(2, invoke(obj, "run")); } + public void testNestPrivateConstructor() throws Exception { + CtClass cc = sloader.get("test5.NestHost3$Builder"); + cc.instrument(new ExprEditor() { + public void edit(NewExpr e) throws CannotCompileException { + String code = "$_ = $proceed($$);"; + e.replace(code); + } + }); + cc.writeFile(); + try { + Class nestHost3Class = cloader.loadClass("test5.NestHost3"); + Object builder = nestHost3Class.getDeclaredMethod("builder").invoke(nestHost3Class); + Class nestHost3BuilderClass = cloader.loadClass("test5.NestHost3$Builder"); + nestHost3BuilderClass.getDeclaredMethod("build").invoke(builder); + } catch (Exception ex) { + ex.printStackTrace(); + fail("it should be able to access the private constructor of the nest host"); + } + } + public void testSwitchCaseWithStringConstant2() throws Exception { CtClass cc = sloader.makeClass("test5.SwitchCase2"); cc.addMethod(CtNewMethod.make( diff --git a/src/test/test5/NestHost3.java b/src/test/test5/NestHost3.java new file mode 100644 index 00000000..912a275c --- /dev/null +++ b/src/test/test5/NestHost3.java @@ -0,0 +1,19 @@ +package test5; + +public class NestHost3 { + private NestHost3(Builder builder) { + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + public Builder() { + } + + public NestHost3 build() { + return new NestHost3(this); + } + } +} -- 2.39.5