@Test
  public void analyzingTwoArgumentsLambda() throws Exception {
    class C {
      void m() {
        λ(s, n, null);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertTrue(lambda.locals.isEmpty());
    assertTrue(lambda.getMutableLocals().isEmpty());

    assertEquals(list("s", "n"), list(lambda.parameters.keySet()));
    assertEquals(list(getType(String.class), INT_TYPE), lambda.getParameterTypes());

    assertEquals(list(object, object), lambda.newLambdaParameterTypes);

    assertEquals(object, lambda.expressionType);
    assertEquals(getType(Fn2.class), lambda.lambdaType);

    assertEquals("call", lambda.sam.getName());
    assertEquals(list(object, object), list(lambda.sam.getArgumentTypes()));
    assertEquals(object, lambda.sam.getReturnType());

    assertFalse(lambda.parameterNeedsUnboxing("s"));
    assertTrue(lambda.parameterNeedsUnboxing("n"));
  }
  @Test
  public void analyzingLambdaClosingOverEffectivelyFinalArgument() throws Exception {
    class C {
      void m(int i) {
        λ(i);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(1, lambda.locals.size());
    assertTrue(lambda.getMutableLocals().isEmpty());

    assertEquals(list("i"), list(lambda.locals.keySet()));
    assertEquals(INT_TYPE, lambda.getLocalVariableType("i"));
  }
  @Test
  public void analyzingLambdaCreatedFromPrimitiveLambdaThatHasDefaultValue() throws Exception {
    class C {
      void m() {
        LambdaPrimitives.λ(d = 2, 0);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(getType(Fn1DtoI.class), lambda.lambdaType);
    assertEquals(list(DOUBLE_TYPE), list(lambda.sam.getArgumentTypes()));
    assertEquals(list(DOUBLE_TYPE), lambda.getParameterTypes());

    assertTrue(lambda.parametersWithDefaultValue.contains("d"));
    assertTrue(lambda.parameterDefaultValueNeedsBoxing("d"));
  }
  @Test
  public void analyzingLambdaClosingOverVariableInitialziedInLambda() throws Exception {
    class C {
      void m() {
        int i;
        λ(i = 2);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(1, lambda.locals.size());
    assertEquals(1, lambda.getMutableLocals().size());

    assertEquals(list("i"), list(lambda.getMutableLocals().keySet()));
    assertEquals(INT_TYPE, lambda.getLocalVariableType("i"));
  }
  @Test
  public void analyzingLambdaClosingOverIncreasedVariableOutsideLambda() throws Exception {
    class C {
      void m() {
        int i = 1;
        λ(i++);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(1, lambda.locals.size());
    assertEquals(1, lambda.getMutableLocals().size());

    assertEquals(list("i"), list(lambda.getMutableLocals().keySet()));
    assertEquals(INT_TYPE, lambda.getLocalVariableType("i"));
  }
  @Test
  public void analyzingLambdaClosingOverMutableVariableMutatedOutsideLambda() throws Exception {
    class C {
      void m() {
        int i = 1;
        i = 2;
        λ(i);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(1, lambda.locals.size());
    assertEquals(1, lambda.getMutableLocals().size());

    assertEquals(list("i"), list(lambda.getMutableLocals().keySet()));
  }
  @Test
  public void analyzingLambdaCreatedFromPrimitiveLambdaThatNeedsNarrowConversionFromLongToInt()
      throws Exception {
    class C {
      void m() {
        LambdaPrimitives.λ(l, idx, 0);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(getType(Fn2LLtoL.class), lambda.lambdaType);

    assertEquals(list(LONG_TYPE, LONG_TYPE), list(lambda.sam.getArgumentTypes()));
    assertEquals(list(LONG_TYPE, INT_TYPE), lambda.getParameterTypes());

    assertFalse(lambda.parameterNeedsUnboxing("l"));
    assertFalse(lambda.parameterNeedsBoxing("l"));
    assertFalse(lambda.parameterNeedsNarrowConversionFromActualArgument("l"));
    assertEquals(-1, lambda.parameterNarrowConversionOpcode("l"));

    assertFalse(lambda.parameterNeedsUnboxing("idx"));
    assertFalse(lambda.parameterNeedsBoxing("idx"));
    assertTrue(lambda.parameterNeedsNarrowConversionFromActualArgument("idx"));
    assertEquals(L2I, lambda.parameterNarrowConversionOpcode("idx"));

    assertEquals(LONG_TYPE, lambda.expressionType);
    assertEquals(LONG_TYPE, lambda.sam.getReturnType());
  }
  @Test
  public void analyzingLambdaCreatedFromGenericCastWithTypesThatNeedConversion() throws Exception {
    class C {
      void m() {
        I i = delegate(n, o1, dbl, 0);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(getType(I.class), lambda.lambdaType);
    assertEquals("invoke", lambda.sam.getName());

    assertEquals(list("n", "o1", "dbl"), list(lambda.parameters.keySet()));

    assertEquals(list(INT_TYPE, object, DOUBLE_TYPE), list(lambda.sam.getArgumentTypes()));
    assertEquals(list(INT_TYPE, object, getType(Double.class)), lambda.getParameterTypes());

    assertFalse(lambda.parameterNeedsUnboxing("n"));
    assertFalse(lambda.parameterNeedsBoxing("n"));

    assertFalse(lambda.parameterNeedsUnboxing("o1"));
    assertFalse(lambda.parameterNeedsBoxing("o1"));

    assertFalse(lambda.parameterNeedsUnboxing("dbl"));
    assertTrue(lambda.parameterNeedsBoxing("dbl"));

    assertEquals(object, lambda.expressionType);
    assertEquals(LONG_TYPE, lambda.sam.getReturnType());

    assertTrue(lambda.returnNeedsUnboxing());
    assertFalse(lambda.returnNeedsBoxing());
  }
  @Test
  public void analyzingLambdaClosingOverMutableArgument() throws Exception {
    class C {
      void m(int i) {
        i = 2;
        i = 4;
        λ(i);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(1, lambda.locals.size());
    assertEquals(1, lambda.getMutableLocals().size());

    assertEquals(list("i"), list(lambda.getMutableLocals().keySet()));
    assertEquals(INT_TYPE, lambda.getLocalVariableType("i"));
  }
  @Test
  public void analyzingTwoArgumentLambdaWithDefaultValue() throws Exception {
    class C {
      void m() {
        λ(n, s = "", null);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(list("n", "s"), list(lambda.parameters.keySet()));
    assertEquals(list(INT_TYPE, getType(String.class)), lambda.getParameterTypes());

    assertFalse(lambda.parametersWithDefaultValue.contains("n"));
    assertTrue(lambda.parametersWithDefaultValue.contains("s"));

    assertTrue(lambda.parameterDefaultValueNeedsBoxing("n"));
    assertFalse(lambda.parameterDefaultValueNeedsBoxing("s"));
  }
  @Test
  public void analyzingOneArgumentLambdaWithDefaultValue() throws Exception {
    class C {
      void m() {
        λ(n = 2, null);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertTrue(lambda.locals.isEmpty());
    assertTrue(lambda.getMutableLocals().isEmpty());

    assertEquals(list("n"), list(lambda.parameters.keySet()));
    assertEquals(list(INT_TYPE), lambda.getParameterTypes());

    assertTrue(lambda.parametersWithDefaultValue.contains("n"));
    assertTrue(lambda.parameterDefaultValueNeedsBoxing("n"));
  }
  @Test
  public void analyzingLambdaCreatedFromPrimitiveLambdaThatNeedsNarrowConversionFromDoubleToFloat()
      throws Exception {
    class C {
      void m() {
        LambdaPrimitives.λ(d, fl, 0);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(getType(Fn2DDtoD.class), lambda.lambdaType);
    assertEquals(list(DOUBLE_TYPE, DOUBLE_TYPE), list(lambda.sam.getArgumentTypes()));
    assertEquals(list(DOUBLE_TYPE, FLOAT_TYPE), lambda.getParameterTypes());

    assertFalse(lambda.parameterNeedsUnboxing("fl"));
    assertFalse(lambda.parameterNeedsBoxing("fl"));
    assertTrue(lambda.parameterNeedsNarrowConversionFromActualArgument("fl"));
    assertEquals(D2F, lambda.parameterNarrowConversionOpcode("fl"));
  }
  @Test
  public void analyzingLambdaCreatedFromGenericCast() throws Exception {
    class C {
      void m() {
        Runnable r = delegate(null);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertTrue(lambda.parameters.isEmpty());
    assertTrue(lambda.newLambdaParameterTypes.isEmpty());

    assertEquals(object, lambda.expressionType);
    assertEquals(getType(Runnable.class), lambda.lambdaType);

    assertEquals("run", lambda.sam.getName());
    assertEquals(0, lambda.sam.getArgumentTypes().length);

    assertEquals(VOID_TYPE, lambda.sam.getReturnType());
    assertFalse(lambda.returnNeedsBoxing());
  }
  @Test
  public void analyzingZeroArgumentLambda() throws Exception {
    class C {
      void m() {
        λ();
      }
    }

    LambdaTreeWeaver weaver = analyze(C.class);
    assertEquals(getType(C.class).getInternalName(), weaver.c.name);
    assertEquals(2, weaver.methods.size());

    MethodAnalyzer constructor = weaver.methods.get(0);
    assertEquals("<init>", constructor.m.name);
    assertTrue(constructor.lambdas.isEmpty());

    MethodAnalyzer method = weaver.methods.get(1);
    assertEquals("m", method.m.name);

    assertEquals(1, method.lambdas.size());
    LambdaAnalyzer lambda = method.lambdas.get(0);

    assertTrue(lambda.locals.isEmpty());
    assertTrue(lambda.getMutableLocals().isEmpty());

    assertTrue(lambda.parameters.isEmpty());
    assertTrue(lambda.newLambdaParameterTypes.isEmpty());

    assertEquals(object, lambda.expressionType);
    assertEquals(getType(Fn0.class), lambda.lambdaType);

    assertEquals("call", lambda.sam.getName());
    assertEquals(0, lambda.sam.getArgumentTypes().length);

    assertEquals(object, lambda.sam.getReturnType());
    assertFalse(lambda.returnNeedsUnboxing());
    assertFalse(lambda.returnNeedsBoxing());
  }
  @Test
  public void analyzingLambdaCreatedFromGenericCastWhichAlsoDefinesMethodFromObject()
      throws Exception {
    class C {
      void m() {
        Comparator<?> c = delegate(o1, o2, 0);
      }
    }

    LambdaAnalyzer lambda = lambdaIn(C.class);

    assertEquals(list("o1", "o2"), list(lambda.parameters.keySet()));
    assertEquals(list(object, object), lambda.newLambdaParameterTypes);
    assertEquals(object, lambda.expressionType);

    assertEquals(getType(Comparator.class), lambda.lambdaType);

    assertEquals("compare", lambda.sam.getName());
    assertEquals(list(object, object), list(lambda.sam.getArgumentTypes()));

    assertEquals(INT_TYPE, lambda.sam.getReturnType());
    assertTrue(lambda.returnNeedsUnboxing());
    assertFalse(lambda.returnNeedsBoxing());
  }