Community
Participate
Working Groups
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
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
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.
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).
I can confirm that it works now. BTW, where is the development Maven repository?
Repo should be at: repo.spring.io/snapshot Thanks for testing the fix.
*** Bug 434272 has been marked as a duplicate of this bug. ***