0

Code in question is below.

private ObjectMapper mapper = new ObjectMapper();
@Override
        public List<GameSession> getAllGameSessions() {
            try {
                return Files.list(Paths.get(BASE_DIR)).filter(path -> path.getFileName().toString().endsWith(FILE_FORMAT_JSON))
                        .map(path -> {
                            try (BufferedReader bufferedReader = Files.newBufferedReader(path)) {
                                return (mapper.readValue(bufferedReader, GameSession.class));
                            } catch (IOException ioException) {
                                ioException.printStackTrace();
                                return null;
                            }
                        }).filter(gameSession -> gameSession!=null).collect(Collectors.toList());
            } catch (IOException e) {
                e.printStackTrace();
                return Collections.emptyList();
            }
        }

When I launch the app (Spring Boot) I get somewhat cryptic style exception which reads as:

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-03-22 20:04:34.269 ERROR 26812 --- [           main] o.s.boot.SpringApplication               : Application startup failed

java.lang.VerifyError: Inconsistent stackmap frames at branch target 49
Exception Details:
  Location:
    com/x/game/repository/FileSystemGameSessionRepositoryImpl.lambda$getAllGameSessions$1(Ljava/nio/file/Path;)Lcom/x/game/model/GameSession; @49: aload
  Reason:
    Type 'java/lang/Object' (current frame, locals[4]) is not assignable to 'com/x/game/model/GameSession' (stack map, locals[4])
  Current Frame:
    bci: @20
    flags: { }
    locals: { 'com/x/game/repository/FileSystemGameSessionRepositoryImpl', 'java/nio/file/Path', 'java/io/BufferedReader', null, 'java/lang/Object' }
    stack: { 'java/io/BufferedReader' }
  Stackmap Frame:
    bci: @49
    flags: { }
    locals: { 'com/x/game/repository/FileSystemGameSessionRepositoryImpl', 'java/nio/file/Path', 'java/io/BufferedReader', 'java/lang/Throwable', 'com/x/game/model/GameSession' }
    stack: { }
  Bytecode:
    0x0000000: 2bb8 0006 4d01 4e2a b400 042c 1207 b600
    0x0000010: 083a 042c c600 1d2d c600 152c b600 0aa7
    0x0000020: 0012 3a05 2d19 05b6 000c a700 072c b600
    0x0000030: 0a19 04b0 3a04 1904 4e19 04bf 3a06 2cc6
    0x0000040: 001d 2dc6 0015 2cb6 000a a700 123a 072d
    0x0000050: 1907 b600 0ca7 0007 2cb6 000a 1906 bf4d
    0x0000060: 2cb6 000e 01b0                         
  Exception Handler Table:
    bci [27, 31] => handler: 34
    bci [7, 19] => handler: 52
    bci [7, 19] => handler: 60
    bci [70, 74] => handler: 77
    bci [52, 62] => handler: 60
    bci [0, 49] => handler: 95
    bci [52, 95] => handler: 95
  Stackmap Table:
    full_frame(@34,{Object[#72],Object[#105],Object[#74],Object[#75],Object[#91]},{Object[#75]})
    same_frame(@45)
    same_frame(@49)
    full_frame(@52,{Object[#72],Object[#105],Object[#74],Object[#75]},{Object[#75]})
    same_locals_1_stack_item_frame(@60,Object[#75])
    full_frame(@77,{Object[#72],Object[#105],Object[#74],Object[#75],Top,Top,Object[#75]},{Object[#75]})
    same_frame(@88)
    same_frame(@92)
    full_frame(@95,{Object[#72],Object[#105]},{Object[#77]})

    at java.lang.Class.getDeclaredMethods0(Native Method) ~[na:1.8.0_121]
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) ~[na:1.8.0_121]
    at java.lang.Class.getDeclaredMethods(Class.java:1975) ~[na:1.8.0_121]
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:613) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]

Clearly the text in question is:

Type 'java/lang/Object' (current frame, locals[4]) is not assignable to 'com/x/game/model/GameSession' (stack map, locals[4])

So am I doing something too clever or Java 8 again has some lambda limitations which I am yet to discover?

IntelliJ is happy, runtime is not. If I replace entire lambda map block with new GameSession(); then happy days.

Aubergine
  • 5,862
  • 19
  • 66
  • 110

3 Answers3

3

I suppose, this is related to the horrible way, javac compiles try(…) constructs, but that’s only a guess. At least, I can confirm that this behavior is reproducible with the following MCVE that doesn’t use any 3rd party code nor byte code transformation tools:

import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Tmp {
    static <T> T readValue(Supplier<T> s, Class<T> type) throws Exception {
        return s.get();
    }
    interface Source extends Supplier<String>, AutoCloseable {
        public default void close() throws Exception {}
    }

    public static void main(String[] args) {
        Stream.of("one", "two", "three")
              .map(s -> {
                  try(Source source = () -> s) {
                      return (readValue(source, String.class));
                  }
                  catch(Exception ex) {
                      return null;
                  }
              })
              .forEach(System.out::println);
    }
}

Testable on Ideone and removing the braces does indeed change the result


As far as I can see, this affects all versions of Java 8 and earlier versions of Java 9 (works with b66 and newer).

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
0

Found the issue... typo in return (mapper.readValue(bufferedReader, GameSession.class));

Extra brackets, I always thought that extra parenthesis doesn't make any difference. Well, the pleasant looking exception is the proof it does. Anyone eager to explain, I will accept the answer explaining this behavior.

Updated question title so others can find solution.

Aubergine
  • 5,862
  • 19
  • 66
  • 110
0

This is most probably nothing related to those parentheses, but instead some bytecode manipulation tool that you are using (may be without even knowing it). It might be JRebel or Spring Boot's Spring Loader, etc.

When you put those parentheses, you probably did something more, like removing the old war, cleaning something, etc. and that is why it worked.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 3
    Believe it or not, the braces *do* make the difference, without any bytecode processing tools/libs involved… – Holger Mar 23 '17 at 12:39