Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8164714

Constructor.newInstance creates instance of inner class with null outer class

XMLWordPrintable

      FULL PRODUCT VERSION :
      openjdk version "1.8.0_91"
      OpenJDK Runtime Environment (build 1.8.0_91-b14)
      OpenJDK 64-Bit Server VM (build 25.91-b14, mixed mode)


      ADDITIONAL OS VERSION INFORMATION :
      Linux godwit.cs.washington.edu 4.4.6-200.fc22.x86_64 #1 SMP Wed Mar 16 22:13:40 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

      A DESCRIPTION OF THE PROBLEM :
      Creating an instance of an inner class always requires a non-null outer class.

      However, it is possible to call Constructor.newInstance() to create an instance of an inner class with a null outer class, at least when the outer class declares no fields.
      Please see the below code, which reproduces the error.

      Proposed resolution: newInstance for an inner class should throw a NullPointerException whenever the first argument is null.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      javac NullOuterTest.java
      java -ea -cp . NullOuterTest

      where NullOuterTest.java is in the "Source code" part of this issue report.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Expected:
      Both calls to invokeReflectively should issue a NullPointerException
      ACTUAL -
      Actual:
      The first call to invokeReflectively completes normally, creating an illegal object.
      The second call to invokeReflectively issues a NullPointerException, as desired.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.lang.reflect.*;

      class ClassWithInnerClass {
        public class A {
          public String toString() {
            return String.format("ClassWithInnerClass.A(outer=%s)",
                                 ClassWithInnerClass.this);
          }
        }
      }

      class ClassWithInnerClassAndField {
        int i;
        public class A {
          public String toString() {
            return String.format("ClassWithInnerClass.A(outer=%s, i=%s)",
                                 ClassWithInnerClassAndField.this, i);
          }
        }
      }

      public class NullOuterTest {
        public static void main(String[] args) {


          System.out.println("Using reflection:");
          // expected: this first invocation should fail with a NullPointerException
          invokeReflectively(ClassWithInnerClass.class, null);
          // this second invocation correctly fails with a NullPointerException
          invokeReflectively(ClassWithInnerClassAndField.class, null);

          System.out.println("Executing directly:");
          try {
            ClassWithInnerClass outerObj = null;
            ClassWithInnerClass.A a2 = outerObj.new A();
            System.out.println("Created object with null outer: " + a2);
          } catch (NullPointerException e) {
            System.out.println("NPE when creating object with null outer");
          }

        }

        private static void invokeReflectively(Class<?> outerClass, Object outerObj) {
          Constructor<?> c = getInnerConstructor(outerClass);
          System.out.println("Constructor: " + c);
          try {
            Object obj = c.newInstance(outerObj);
            System.out.println("Created object with null outer: " + obj);
          } catch (Throwable e) {
            System.out.printf("failure calling constructor; type=%s; message=%s%n",
                              e.getClass(), e.getMessage());
          }
        }

        private static Constructor<?> getInnerConstructor(Class<?> outerClass) {
          Constructor<?> constructor = null;
          Class<?>[] innerClasses = outerClass.getDeclaredClasses();
          assert innerClasses.length == 1: "should only be one inner class";
          Class<?> innerClass = innerClasses[0];
          try {
            constructor = innerClass.getConstructor(outerClass);
          } catch (NoSuchMethodException e) {
            assert false : "no constructor; message=" + e.getMessage();
          }
          return constructor;
        }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Clients can check whether newInstance is called on an inner class constructor and whether the first argument is null. If both are true, the client should not perform the call.

            liach Chen Liang
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: