13

Running the code below results in the error message Bad type on operand stack.

public static void main(String args[]) {
        TransformService transformService = (inputs) -> {
            return new ArrayList<String>(3) {{
                add("one");
                add("two");
                add("three");
            }};
        };

        Collection<Integer> inputs = new HashSet<Integer>(2) {{
            add(5);
            add(7);
        }};
        Collection<String> results = transformService.transform(inputs);
        System.out.println(results.size());
    }

    public interface TransformService {
        Collection<String> transform(Collection<Integer> inputs);
    }

However removing the double brace initialization (anonymous inner classes) within the lamda allows the code to run as expected, why ? The below works :

public class SecondLambda {
    public static void main(String args[]) {
        TransformService transformService = (inputs) -> {
            Collection<String> results = new ArrayList<String>(3);
            results.add("one");
            results.add("two");
            results.add("three");

            return results;
        };

        Collection<Integer> inputs = new HashSet<Integer>(2) {{
            add(5);
            add(7);
        }};
        Collection<String> results = transformService.transform(inputs);
        System.out.println(results.size());
    }

    public interface TransformService {
        Collection<String> transform(Collection<Integer> inputs);
    }
}

Compiler bug ? It is the early access version after all ...

(This won't compile unless you have the latest jdk 8 lambda download.)

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
  • The tag lambda is meant for languages other than Java like C#, C++. – Lion Nov 04 '12 at 14:18
  • 8
    @Lion: I believe that Java 8 implements lambdas (I've never used it, so I don't know this for a fact), and so this tag appears to be appropriate for the question. – Hovercraft Full Of Eels Nov 04 '12 at 14:19
  • 11
    @Lion that's only because Java hasn't had lambdas before. A lambda is a lambda, even if it's in an alpha version of the language. – Marko Topolnik Nov 04 '12 at 14:22
  • Shouldn't you declare it `TransformService transformService = ...`? – assylias Nov 04 '12 at 14:30
  • @assylias yes (although there was no compiler error actually) see update, still getting the same error – NimChimpsky Nov 04 '12 at 14:33
  • what is lambada? what you can do with it ? – someone Nov 04 '12 at 14:59
  • I don't have a specific answer to your question, but my first thought is to start by looking at the fact that using the static initializer means you wind up with an (anonymous) subclass of HashSet rather than an instance of HashSet. It doesn't seem like that should make a difference, but that's the most obvious difference between the working and non working code to me. – RHSeeger Nov 04 '12 at 15:32
  • 3
    Check what happens if you add the anonymous inner class back in one place but not the other. My guess is that making `inputs` an anonymous class is okay but making the return value an anonymous class isn't. If that's the case, it's the same thing reported at http://mail.openjdk.java.net/pipermail/lambda-dev/2012-September/005938.html. There's no followup there, but it sure does look like a compiler bug. – jacobm Nov 04 '12 at 15:34
  • @jacobm yes that is correct. will update question. – NimChimpsky Nov 04 '12 at 15:43

3 Answers3

7

It seems, that problem occurs not only in case when lambda returns anonymous type, but even if any anonymous class is constructed inside lambda. I.e.:

public class TestLambda {
    public static void main(String[] args) {
        xxx();
    }
    static void xxx() {
        Functional1 f  = () -> {
            Object o = new Object() { };
            return new A();
        };
    }
    static class A { }
    static interface Functional1 { A func(); }
}

This actually leads to Exception in thread "main" java.lang.VerifyError: Bad local variable type (...) Reason: Type top (current frame, locals[0]) is not assignable to reference type.

Further investigation shows, that if we will introduce parameter into method xxx, the reason for an exception will contains its type. E.g.:

Type 'java/lang/Integer' (current frame, stack[0]) is not assignable to 'lambda/TestLambda'

And this is already very interesting. Let's change type of xxx parameter (which is not actually used) to type of top class, i.e. TestLambda:

...
    xxx(new TestLambda());
}
private static void xxx(TestLambda x) {
...

And what do you think? This fixes the problem! Everything begin work well. Even, if we will change return A(); to return new A() {};. Check this!


My conclusion is that this is real JVM bug. It seems, that the problem is with stack of loaded classes. It occurs in combine with method, which Java uses for translating lambda expressions (http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html) - it produces synthetic methods inside top class. It seems, that when anonymous classes are introduced in lambda stack becomes broken. It could be fixed using mentioned workaround.

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
2

Compiler bug ? It is the early access version after all ...

I would say that any error message that mentions the operand stack is highly likely to be a due to a compiler bug or a bug in the JVM. Especially if you can get it using a pure Java example.

(It looks like the JVM is reporting a typesafety problem that should have been detected by the compiler, and/or the bytecode verifier at class-load time.)

Report it via the recommended channel for Java 8 bugs.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

Not directly related to your issue, but I'd strongly recommend not using anonymous classes in this way. You're creating an entirely new HashSet subtype solely for the purpose of adding two values to it. Not only does this bloat up the system (it stays in memory forever) it can also confound the JVM's JIT since it never just sees HashSet at a call site...it sees one of many subtypes you've created.