Bug 435446 - ClassFormatError when targetting nested forEach lambda expressions
Summary: ClassFormatError when targetting nested forEach lambda expressions
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.8.0   Edit
Hardware: PC Windows 8
: P3 normal (vote)
Target Milestone: 1.8.1   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 434272 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-05-21 16:26 EDT by Alexander Kriegisch CLA
Modified: 2014-06-17 17:33 EDT (History)
2 users (show)

See Also:


Attachments
Sample source code (1.34 KB, application/x-zip-compressed)
2014-05-21 16:26 EDT, Alexander Kriegisch CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Kriegisch CLA 2014-05-21 16:26:52 EDT
Created attachment 243368 [details]
Sample source code

I tried to help somebody on http://stackoverflow.com/questions/23787068/aspect-breaking-bytecode-on-specific-class. I was able to reduce his sample code to something simple. Obviously there are problems with Java 8 when using nested 'forEach' loops with lambda expressions.

Here is my sample application:

package de.scrum_master.app;

import java.util.HashMap;
import java.util.Map;

public class Application {
	public static void main(String[] args) {
		Map<String, Map<Integer, String>> languages = new HashMap<>();

		Map<Integer, String> englishNumbers = new HashMap<>();
		englishNumbers.put(11, "eleven");
		englishNumbers.put(12, "twelve");
		englishNumbers.put(13, "thirteen");
		languages.put("EN", englishNumbers);

		Map<Integer, String> germanNumbers = new HashMap<>();
		germanNumbers.put(11, "elf");
		germanNumbers.put(12, "zwölf");
		germanNumbers.put(13, "dreizehn");
		languages.put("DE", germanNumbers);

		languages.entrySet().stream().forEach((language) -> {
			String languageCode = language.getKey();
			Map<Integer, String> numbers = language.getValue();
			System.out.println("Language code = " + languageCode);
//			numbers.entrySet().stream().forEach((number) -> {
//				int numericValue = number.getKey();
//				String textualValue = number.getValue();
//				System.out.println("  " + numericValue + " -> " + textualValue);
//			});
		});
	}
}


And here is a simple aspect:

package de.scrum_master.aspect;

import de.scrum_master.app.Application;

public aspect MyAspect {
	before() : execution(* Application.*(..)) {
		System.out.println(thisJoinPointStaticPart);
	}
}


This works as expected, the output is:

execution(void de.scrum_master.app.Application.main(String[]))
execution(void de.scrum_master.app.Application.lambda$0(Map.Entry))
Language code = DE
execution(void de.scrum_master.app.Application.lambda$0(Map.Entry))
Language code = EN


But now uncomment the inner 'forEach' loop, i.e. this part:

			numbers.entrySet().stream().forEach((number) -> {
				int numericValue = number.getKey();
				String textualValue = number.getValue();
				System.out.println("  " + numericValue + " -> " + textualValue);
			});


This yields the following exception when starting the driver application:

Exception in thread "main" java.lang.ClassFormatError: Invalid length 65517 in LocalVariableTable in class file de/scrum_master/app/Application
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:367)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)


A dirty workaround is to exclude the lambda methods in the pointcut like this:

	before() : execution(* Application.*(..)) && !execution(* *.lambda$*(..)) {
		System.out.println(thisJoinPointStaticPart);
	}


Now it works again and the output is:

execution(void de.scrum_master.app.Application.main(String[]))
Language code = DE
  11 -> elf
  12 -> zwölf
  13 -> dreizehn
Language code = EN
  11 -> eleven
  12 -> twelve
  13 -> thirteen
Comment 1 Alexander Kriegisch CLA 2014-05-22 03:34:41 EDT
The problem really seems to be the Eclipse Java compiler because if I compile my application class separately with javac and perform post-compile weaving upon it, it works with nested lambda 'forEach' and without excluding the lambdas from the pointcut:

execution(void de.scrum_master.app.Application.main(String[]))
execution(void de.scrum_master.app.Application.lambda$main$1(Map.Entry))
Language code = DE
execution(void de.scrum_master.app.Application.lambda$null$0(Map.Entry))
  11 -> elf
execution(void de.scrum_master.app.Application.lambda$null$0(Map.Entry))
  12 -> zwölf
execution(void de.scrum_master.app.Application.lambda$null$0(Map.Entry))
  13 -> dreizehn
execution(void de.scrum_master.app.Application.lambda$main$1(Map.Entry))
Language code = EN
execution(void de.scrum_master.app.Application.lambda$null$0(Map.Entry))
  11 -> eleven
execution(void de.scrum_master.app.Application.lambda$null$0(Map.Entry))
  12 -> twelve
execution(void de.scrum_master.app.Application.lambda$null$0(Map.Entry))
  13 -> thirteen
Comment 2 Alexander Kriegisch CLA 2014-05-22 08:21:46 EDT
Another workaround is to use JVM option '-noverify'. In Java 8 '-XX:-UseSplitVerifier' is no longer a valid option, but the other one also works. A solution would be preferable to a workaround though.
Comment 3 Andrew Clement CLA 2014-05-23 16:39:57 EDT
All fixed up. Thanks for the great test case.

The problem was the size of the invokedynamic instruction was not being calculated correctly. This led to a bad calculation of the local variable entry.  Latest dev builds include the fix (on the website and in the snapshot maven repo).
Comment 4 Alexander Kriegisch CLA 2014-05-24 03:14:57 EDT
I can confirm that it works now. BTW, where is the development Maven repository?
Comment 5 Andrew Clement CLA 2014-05-24 03:18:48 EDT
Repo should be at: repo.spring.io/snapshot

Thanks for testing the fix.
Comment 6 Andrew Clement CLA 2014-06-17 17:33:26 EDT
*** Bug 434272 has been marked as a duplicate of this bug. ***