JDK-8129740 : Incorrect class file created when passing lambda in inner class constructor
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8-pool,9
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2015-05-21
  • Updated: 2017-07-20
  • Resolved: 2015-11-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u112Fixed 9 b93Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
Reproduced on all of these:

java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

java version "1.9.0-ea"
Java(TM) SE Runtime Environment (build 1.9.0-ea-b57)
Java HotSpot(TM) 64-Bit Server VM (build 1.9.0-ea-b57, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7600]

A DESCRIPTION OF THE PROBLEM :
I compile the following class (CompilerBug.java)

public class CompilerBug {
	int var = 0;

	public static void main(String[] args) {
		new CompilerBug().new Inner();
	}

	public class Inner {
		public Inner(Runnable r) {}

		public Inner() {
			this(() -> {
				var = 1;
			});
		}
	}
}

Compilation finishes without errors. However I see a VerifyError when running the compiled CompilerBug.class

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create file CompilerBug.java (see "source code" section):

2. Compile it:
javac CompilerBug.java 

3. Run the compiled class:
java CompilerBug

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Compilation error on step 2 like it's reported by ECJ:

1. ERROR in C:\projects\Test\src\CompilerBug.java (at line 12)
	this(() -> {
	     ^^^^^
Cannot refer to 'this' nor 'super' while explicitly invoking a constructor

ACTUAL -
Compilation goes fine, but when compiled class is launched (step 3) the following error is displayed:

Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    CompilerBug$Inner.<init>(LCompilerBug;)V @3: invokedynamic
  Reason:
    Type uninitializedThis (current frame, stack[2]) is not assignable to 'CompilerBug$Inner'
  Current Frame:
    bci: @3
    flags: { flagThisUninit }
    locals: { uninitializedThis, 'CompilerBug' }
    stack: { uninitializedThis, 'CompilerBug', uninitializedThis }
  Bytecode:
    0000000: 2a2b 2aba 0003 0000 b700 04b1

        at CompilerBug.main(CompilerBug.java:5)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class CompilerBug {
	int var = 0;

	public static void main(String[] args) {
		new CompilerBug().new Inner();
	}

	public class Inner {
		public Inner(Runnable r) {}

		public Inner() {
			this(() -> {
				var = 1;
			});
		}
	}
}

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


Comments
Work in progress. Works for the reported case, but needs polish.
08-10-2015

An alternate fix would be to promote the lambda implementation method to the outer class.
08-10-2015

This is a very interesting problem and ECJ behavior is incorrect. 8.8.7.1 states: An explicit constructor invocation statement in a constructor body may not refer to any instance variables or instance methods or inner classes declared in this class or any superclass, or use this or super in any expression; otherwise, a compiletime error occurs. This does not forbid use of variables, types, and methods from an enclosing instance which must be fully constructed anyway before inner class constructor can be entered. Javac's woes in the case of the problem snippet arise from first concluding that the lambda must capture `this' that corresponds to Inner (it only needs to capture outer enclosing instance that corresponds to CompilerBug) from there deciding to make the implementation method an instance method of Inner and then on having to materialize Inner.this before the indy call. At that point Inner.this is uninitializedThis and cannot be passed around. Fix would involve making the lambda implementation method a static method and arranging to capture and pass around outer instance handles and rewriting instance member accesses to go through the synthetic locals. As an aside see that when the lambda is transformed to an anonymous class both ECJ and javac compile it fine.
08-10-2015

https://bugs.openjdk.java.net/browse/JDK-8133111
07-10-2015

Checked this with JDK 8, 8u40, 8u45, 8u60 ea b19, and 9 ea b69 as well and could reproduce the issue on Windows 7 and Linux (64-bit). -------------------------- Output with JDK 8u45: $ java CompilerBug Exception in thread "main" java.lang.VerifyError: Bad type on operand stack Exception Details: Location: CompilerBug$Inner.<init>(LCompilerBug;)V @3: invokedynamic Reason: Type uninitializedThis (current frame, stack[2]) is not assignable to 'CompilerBug$Inner' Current Frame: bci: @3 flags: { flagThisUninit } locals: { uninitializedThis, 'CompilerBug' } stack: { uninitializedThis, 'CompilerBug', uninitializedThis } Bytecode: 0000000: 2a2b 2aba 0003 0000 b700 04b1 at CompilerBug.main(CompilerBug.java:5)
24-06-2015

Submitter pinged me about this bug. It reproduces perfectly well on today's self-built jdk9-dev/release, Linux x86_64. Please move forward with this bug. Thanks!
23-06-2015