Package com.google.caja.ancillary.linter

Source Code of com.google.caja.ancillary.linter.VariableLivenessTest

// Copyright (C) 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.caja.ancillary.linter;

import com.google.caja.parser.AncestorChain;
import com.google.caja.parser.ParseTreeNode;
import com.google.caja.parser.js.ReturnStmt;
import com.google.caja.parser.js.Statement;
import com.google.caja.parser.js.ThrowStmt;
import com.google.caja.reporting.MessageContext;
import com.google.caja.util.CajaTestCase;
import com.google.caja.util.MoreAsserts;

import java.io.IOException;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;

/**
* @author mikesamuel@gmail.com
*/
public class VariableLivenessTest extends CajaTestCase {
  public final void testSimpleDeclaration()  throws Exception {
    assertLiveness(
        js(fromString("var x = 1; return x;")),
        "Block",
        "  Declaration",
        "    Identifier : x",
        "    IntegerLiteral : 1",
        "  ReturnStmt ; liveness=(x)",
        "    Reference ; liveness=(x)",
        "      Identifier : x"
        );
  }

  public final void testSimpleAssignment()  throws Exception {
    assertLiveness(
        js(fromString("var x, y; x = 1; return x;")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : x",
        "    Declaration",
        "      Identifier : y",
        "  ExpressionStmt",
        "    AssignOperation : ASSIGN",
        "      Reference",
        "        Identifier : x",
        "      IntegerLiteral : 1",
        "  ReturnStmt ; liveness=(x)",
        "    Reference ; liveness=(x)",
        "      Identifier : x"
        );
  }

  public final void testConditionWithElse1() throws Exception {
    assertLiveness(
        js(fromString("var x; if (r)  x = 1;  else  x = 2;  return x;")),
        "Block",
        "  Declaration",
        "    Identifier : x",
        "  Conditional",
        "    Reference",
        "      Identifier : r",
        "    ExpressionStmt",
        "      AssignOperation : ASSIGN",
        "        Reference",
        "          Identifier : x",
        "        IntegerLiteral : 1",
        "    ExpressionStmt",
        "      AssignOperation : ASSIGN",
        "        Reference",
        "          Identifier : x",
        "        IntegerLiteral : 2",
        "  ReturnStmt ; liveness=(x)",
        "    Reference ; liveness=(x)",
        "      Identifier : x"
        );
  }

  public final void testConditionWithElse2() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var x = 1, y, z, w;"
            + "if (r && (w = 1))"
            // w is live here
            + "  z = 1, y = 3;"
            + "else"
            // w is not live here
            + "  z = 2;"
            // x is live here since it is assigned early.
            // z is live here since it is assigned in all branches
            // neither w nor y are assigned in all branches so are not live.
            + "return;"
            )),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : x",
        "      IntegerLiteral : 1",
        "    Declaration ; liveness=(x)",
        "      Identifier : y",
        "    Declaration ; liveness=(x)",
        "      Identifier : z",
        "    Declaration ; liveness=(x)",
        "      Identifier : w",
        "  Conditional ; liveness=(x)",
        "    ControlOperation : LOGICAL_AND ; liveness=(x)",
        "      Reference ; liveness=(x)",
        "        Identifier : r",
        "      AssignOperation : ASSIGN ; liveness=(x)",
        "        Reference ; liveness=(x)",
        "          Identifier : w",
        "        IntegerLiteral : 1 ; liveness=(x)",
        "    ExpressionStmt ; liveness=(x w)",
        "      SpecialOperation : COMMA ; liveness=(x w)",
        "        AssignOperation : ASSIGN ; liveness=(x w)",
        "          Reference ; liveness=(x w)",
        "            Identifier : z",
        "          IntegerLiteral : 1 ; liveness=(x w)",
        "        AssignOperation : ASSIGN ; liveness=(x w z)",
        "          Reference ; liveness=(x w z)",
        "            Identifier : y",
        "          IntegerLiteral : 3 ; liveness=(x w z)",
        "    ExpressionStmt ; liveness=(x)",
        "      AssignOperation : ASSIGN ; liveness=(x)",
        "        Reference ; liveness=(x)",
        "          Identifier : z",
        "        IntegerLiteral : 2 ; liveness=(x)",
        "  ReturnStmt ; liveness=(x z)"  // z is assigned in both branches
        );
  }

  public final void testConditionWithoutElse() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var x = 1, y, z;"
            + "if (r)"
            + "  z = 1, y = 3;"
            + "return;"
            )),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : x",
        "      IntegerLiteral : 1",
        "    Declaration ; liveness=(x)",
        "      Identifier : y",
        "    Declaration ; liveness=(x)",
        "      Identifier : z",
        "  Conditional ; liveness=(x)",
        "    Reference ; liveness=(x)",
        "      Identifier : r",
        "    ExpressionStmt ; liveness=(x)",
        "      SpecialOperation : COMMA ; liveness=(x)",
        "        AssignOperation : ASSIGN ; liveness=(x)",
        "          Reference ; liveness=(x)",
        "            Identifier : z",
        "          IntegerLiteral : 1 ; liveness=(x)",
        "        AssignOperation : ASSIGN ; liveness=(x z)",
        "          Reference ; liveness=(x z)",
        "            Identifier : y",
        "          IntegerLiteral : 3 ; liveness=(x z)",
        "  ReturnStmt ; liveness=(x)"  // z is not assigned in both branches
        );
  }

  public final void testConditionWithReturn() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var x = 1, y, z;"
            + "if (r)"
            + "  z = 1, y = 3;"
            + "else"
            + "  return;"
            + "return z;"
            )),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : x",
        "      IntegerLiteral : 1",
        "    Declaration ; liveness=(x)",
        "      Identifier : y",
        "    Declaration ; liveness=(x)",
        "      Identifier : z",
        "  Conditional ; liveness=(x)",
        "    Reference ; liveness=(x)",
        "      Identifier : r",
        "    ExpressionStmt ; liveness=(x)",
        "      SpecialOperation : COMMA ; liveness=(x)",
        "        AssignOperation : ASSIGN ; liveness=(x)",
        "          Reference ; liveness=(x)",
        "            Identifier : z",
        "          IntegerLiteral : 1 ; liveness=(x)",
        "        AssignOperation : ASSIGN ; liveness=(x z)",
        "          Reference ; liveness=(x z)",
        "            Identifier : y",
        "          IntegerLiteral : 3 ; liveness=(x z)",
        "    ReturnStmt ; liveness=(x)",
        // z is assigned in exiting branches
        "  ReturnStmt ; liveness=(x z y)",
        "    Reference ; liveness=(x z y)",
        "      Identifier : z"
        );
  }

  public final void testTernaryOp() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b, c;"
            + "((a = x) || (b = y)) ? (c = z) : (c = w);"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "    Declaration",
        "      Identifier : c",
        "  ExpressionStmt",
        "    ControlOperation : TERNARY",
        "      ControlOperation : LOGICAL_OR",
        "        AssignOperation : ASSIGN",
        "          Reference",
        "            Identifier : a",
        "          Reference",
        "            Identifier : x",
        "        AssignOperation : ASSIGN ; liveness=(a)",
        "          Reference ; liveness=(a)",
        "            Identifier : b",
        "          Reference ; liveness=(a)",
        "            Identifier : y",
        "      AssignOperation : ASSIGN ; liveness=(a)",
        "        Reference ; liveness=(a)",
        "          Identifier : c",
        "        Reference ; liveness=(a)",
        "          Identifier : z",
        "      AssignOperation : ASSIGN ; liveness=(a b)",
        "        Reference ; liveness=(a b)",
        "          Identifier : c",
        "        Reference ; liveness=(a b)",
        "          Identifier : w",
        "  Noop ; liveness=(a c)"
        );
  }

  public final void testForLoop() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "for (var i = 0, j, k = 3, n; i < (n = arr.length); ++j, ++k) {"
            + "  var v = arr[i], e;"
            + "  if (v) {"
            + "    e = 3;"
            + "  } else {"
            + "    break;"
            + "  }"
            + "  ;"
            + "}"
            + ";"
            )),
        "Block",
        "  ForLoop : ",
        "    MultiDeclaration",
        "      Declaration",
        "        Identifier : i",
        "        IntegerLiteral : 0",
        "      Declaration ; liveness=(i)",
        "        Identifier : j",
        "      Declaration ; liveness=(i)",
        "        Identifier : k",
        "        IntegerLiteral : 3 ; liveness=(i)",
        "      Declaration ; liveness=(i k)",
        "        Identifier : n",
        "    SimpleOperation : LESS_THAN ; liveness=(i k)",
        "      Reference ; liveness=(i k)",
        "        Identifier : i",
        "      AssignOperation : ASSIGN ; liveness=(i k)",
        "        Reference ; liveness=(i k)",
        "          Identifier : n",
        "        SpecialOperation : MEMBER_ACCESS ; liveness=(i k)",
        "          Reference ; liveness=(i k)",
        "            Identifier : arr",
        "          Reference ; liveness=(i k)",
        "            Identifier : length",
        "    ExpressionStmt ; liveness=(i k n v e)",
        "      SpecialOperation : COMMA ; liveness=(i k n v e)",
        "        AssignOperation : PRE_INCREMENT ; liveness=(i k n v e)",
        "          Reference ; liveness=(i k n v e)",
        "            Identifier : j",
        "        AssignOperation : PRE_INCREMENT ; liveness=(i k n v e j)",
        "          Reference ; liveness=(i k n v e j)",
        "            Identifier : k",
        "    Block ; liveness=(i k n)",
        "      MultiDeclaration ; liveness=(i k n)",
        "        Declaration ; liveness=(i k n)",
        "          Identifier : v",
        "          SpecialOperation : SQUARE_BRACKET ; liveness=(i k n)",
        "            Reference ; liveness=(i k n)",
        "              Identifier : arr",
        "            Reference ; liveness=(i k n)",
        "              Identifier : i",
        "        Declaration ; liveness=(i k n v)",
        "          Identifier : e",
        "      Conditional ; liveness=(i k n v)",
        "        Reference ; liveness=(i k n v)",
        "          Identifier : v",
        "        Block ; liveness=(i k n v)",
        "          ExpressionStmt ; liveness=(i k n v)",
        "            AssignOperation : ASSIGN ; liveness=(i k n v)",
        "              Reference ; liveness=(i k n v)",
        "                Identifier : e",
        "              IntegerLiteral : 3 ; liveness=(i k n v)",
        "        Block ; liveness=(i k n v)",
        "          BreakStmt :  ; liveness=(i k n v)",
        "      Noop ; liveness=(i k n v e)"// e is assigned here due to break
        "  Noop ; liveness=(i k n)"
        );
  }

  public final void testForLoopThatDoesNotComplete() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b;"
            + "x: for (var i = 0; ; a = 1) {"
            + "  b = 1;"
            + "  break x;"
            + "}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "  ForLoop : x",
        "    Declaration",
        "      Identifier : i",
        "      IntegerLiteral : 0",
        "    BooleanLiteral : true ; liveness=(i)",
        // Since body always exits, increment is unreachable
        "    ExpressionStmt",
        "      AssignOperation : ASSIGN",
        "        Reference",
        "          Identifier : a",
        "        IntegerLiteral : 1",
        "    Block ; liveness=(i)",
        "      ExpressionStmt ; liveness=(i)",
        "        AssignOperation : ASSIGN ; liveness=(i)",
        "          Reference ; liveness=(i)",
        "            Identifier : b",
        "          IntegerLiteral : 1 ; liveness=(i)",
        "      BreakStmt : x ; liveness=(i b)",
        "  Noop ; liveness=(i)");
  }

  public final void testForEachLoop() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a = 1, b, c;"
            + "for (var k in (b = obj)) {"
            + "  c = 1;"
            + "}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "      IntegerLiteral : 1",
        "    Declaration ; liveness=(a)",
        "      Identifier : b",
        "    Declaration ; liveness=(a)",
        "      Identifier : c",
        "  ForEachLoop :  ; liveness=(a)",
        "    Declaration ; liveness=(a b)",
        "      Identifier : k",
        "    AssignOperation : ASSIGN ; liveness=(a)",
        "      Reference ; liveness=(a)",
        "        Identifier : b",
        "      Reference ; liveness=(a)",
        "        Identifier : obj",
        "    Block ; liveness=(a b k)",
        "      ExpressionStmt ; liveness=(a b k)",
        "        AssignOperation : ASSIGN ; liveness=(a b k)",
        "          Reference ; liveness=(a b k)",
        "            Identifier : c",
        "          IntegerLiteral : 1 ; liveness=(a b k)",
        "  Noop ; liveness=(a b)"
        );
  }

  public final void testDoWhileLoop() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b = 0, c, d;"
            + "do {"
            + "  a += 1;"  // b is live here
            + "} while (c = d + 1);"  // a and b live here
            + ";"  // a, b, and c are all live here
            )),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "      IntegerLiteral : 0",
        "    Declaration ; liveness=(b)",
        "      Identifier : c",
        "    Declaration ; liveness=(b)",
        "      Identifier : d",
        "  DoWhileLoop :  ; liveness=(b)",
        "    Block ; liveness=(b)",
        "      ExpressionStmt ; liveness=(b)",
        "        AssignOperation : ASSIGN_SUM ; liveness=(b)",
        "          Reference ; liveness=(b)",
        "            Identifier : a",
        "          IntegerLiteral : 1 ; liveness=(b)",
        "    AssignOperation : ASSIGN ; liveness=(b a)",
        "      Reference ; liveness=(b a)",
        "        Identifier : c",
        "      SimpleOperation : ADDITION ; liveness=(b a)",
        "        Reference ; liveness=(b a)",
        "          Identifier : d",
        "        IntegerLiteral : 1 ; liveness=(b a)",
        "  Noop ; liveness=(b a c)"
        );
  }

  public final void testWhileLoop() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b = 0, c, d;"
            + "while (c = d + 1) {"  // b is live here
            + "  a += 1;"  // b and c are live here
            + "  ;"
            + "}"
            + ";"  // b and c are live here, but not a
            )),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "      IntegerLiteral : 0",
        "    Declaration ; liveness=(b)",
        "      Identifier : c",
        "    Declaration ; liveness=(b)",
        "      Identifier : d",
        "  WhileLoop :  ; liveness=(b)",
        "    AssignOperation : ASSIGN ; liveness=(b)",
        "      Reference ; liveness=(b)",
        "        Identifier : c",
        "      SimpleOperation : ADDITION ; liveness=(b)",
        "        Reference ; liveness=(b)",
        "          Identifier : d",
        "        IntegerLiteral : 1 ; liveness=(b)",
        "    Block ; liveness=(b c)",
        "      ExpressionStmt ; liveness=(b c)",
        "        AssignOperation : ASSIGN_SUM ; liveness=(b c)",
        "          Reference ; liveness=(b c)",
        "            Identifier : a",
        "          IntegerLiteral : 1 ; liveness=(b c)",
        "      Noop ; liveness=(b c a)",
        "  Noop ; liveness=(b c)"
        );
  }

  public final void testTryCatchFinally() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b = 0, c, d;"
            + "try {"
            + "  a = f();"  // Cannot assume this completed later
            + "  ;"
            + "} catch (e) {"
            + "  c = e;"
            + "  return;"  // has no effect
            + "} finally {"
            + "  d = 1;"
            + "  ;"
            + "}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "      IntegerLiteral : 0",
        "    Declaration ; liveness=(b)",
        "      Identifier : c",
        "    Declaration ; liveness=(b)",
        "      Identifier : d",
        "  TryStmt ; liveness=(b)",
        "    Block ; liveness=(b)",
        "      ExpressionStmt ; liveness=(b)",
        "        AssignOperation : ASSIGN ; liveness=(b)",
        "          Reference ; liveness=(b)",
        "            Identifier : a",
        "          SpecialOperation : FUNCTION_CALL ; liveness=(b)",
        "            Reference ; liveness=(b)",
        "              Identifier : f",
        "      Noop ; liveness=(b a)",
        "    CatchStmt ; liveness=(b)",
        "      Declaration ; liveness=(b)",
        "        Identifier : e",
        "      Block ; liveness=(b e@2)",
        "        ExpressionStmt ; liveness=(b e@2)",
        "          AssignOperation : ASSIGN ; liveness=(b e@2)",
        "            Reference ; liveness=(b e@2)",
        "              Identifier : c",
        "            Reference ; liveness=(b e@2)",
        "              Identifier : e",
        "        ReturnStmt ; liveness=(b e@2 c)",
        "    FinallyStmt ; liveness=(b)",
        "      Block ; liveness=(b)",
        "        ExpressionStmt ; liveness=(b)",
        "          AssignOperation : ASSIGN ; liveness=(b)",
        "            Reference ; liveness=(b)",
        "              Identifier : d",
        "            IntegerLiteral : 1 ; liveness=(b)",
        "        Noop ; liveness=(b d)",
        "  Noop ; liveness=(b d)"
        );
  }

  public final void testTryCatch() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b = 0, c, d;"
            + "try {"
            + "  a = f();"  // Cannot assume this completed later
            + "  c = 1;"
            + "} catch (e) {"
            + "  c = d = e;"
            + "}"
            + ";"  // Since c assigned in both, it is live here
            )),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "      IntegerLiteral : 0",
        "    Declaration ; liveness=(b)",
        "      Identifier : c",
        "    Declaration ; liveness=(b)",
        "      Identifier : d",
        "  TryStmt ; liveness=(b)",
        "    Block ; liveness=(b)",
        "      ExpressionStmt ; liveness=(b)",
        "        AssignOperation : ASSIGN ; liveness=(b)",
        "          Reference ; liveness=(b)",
        "            Identifier : a",
        "          SpecialOperation : FUNCTION_CALL ; liveness=(b)",
        "            Reference ; liveness=(b)",
        "              Identifier : f",
        "      ExpressionStmt ; liveness=(b a)",
        "        AssignOperation : ASSIGN ; liveness=(b a)",
        "          Reference ; liveness=(b a)",
        "            Identifier : c",
        "          IntegerLiteral : 1 ; liveness=(b a)",
        "    CatchStmt ; liveness=(b)",
        "      Declaration ; liveness=(b)",
        "        Identifier : e",
        "      Block ; liveness=(b e@2)",
        "        ExpressionStmt ; liveness=(b e@2)",
        "          AssignOperation : ASSIGN ; liveness=(b e@2)",
        "            Reference ; liveness=(b e@2)",
        "              Identifier : c",
        "            AssignOperation : ASSIGN ; liveness=(b e@2)",
        "              Reference ; liveness=(b e@2)",
        "                Identifier : d",
        "              Reference ; liveness=(b e@2)",
        "                Identifier : e",
        "  Noop ; liveness=(b c)"
        );
  }

  public final void testTryCatchWithReturn() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b = 0, c;"
            + "try {"
            + "  a = f();"  // Can assume this completed later
            + "} catch (e) {"
            + "  return null;"  // because if it failed, we would have returned
            + "}"
            + ";"  // a and b are both visible here
            )),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "      IntegerLiteral : 0",
        "    Declaration ; liveness=(b)",
        "      Identifier : c",
        "  TryStmt ; liveness=(b)",
        "    Block ; liveness=(b)",
        "      ExpressionStmt ; liveness=(b)",
        "        AssignOperation : ASSIGN ; liveness=(b)",
        "          Reference ; liveness=(b)",
        "            Identifier : a",
        "          SpecialOperation : FUNCTION_CALL ; liveness=(b)",
        "            Reference ; liveness=(b)",
        "              Identifier : f",
        "    CatchStmt ; liveness=(b)",
        "      Declaration ; liveness=(b)",
        "        Identifier : e",
        "      Block ; liveness=(b e@2)",
        "        ReturnStmt ; liveness=(b e@2)",
        "          NullLiteral : null ; liveness=(b e@2)",
        "  Noop ; liveness=(b a)"
        );
  }

  public final void testTryFinally() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b = 0, d;"
            + "try {"
            + "  a = f();"  // Cannot assume this completed later
            + "  ;"
            + "} finally {"
            + "  d = 1;"
            + "  ;"
            + "}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "      IntegerLiteral : 0",
        "    Declaration ; liveness=(b)",
        "      Identifier : d",
        "  TryStmt ; liveness=(b)",
        "    Block ; liveness=(b)",
        "      ExpressionStmt ; liveness=(b)",
        "        AssignOperation : ASSIGN ; liveness=(b)",
        "          Reference ; liveness=(b)",
        "            Identifier : a",
        "          SpecialOperation : FUNCTION_CALL ; liveness=(b)",
        "            Reference ; liveness=(b)",
        "              Identifier : f",
        "      Noop ; liveness=(b a)",
        "    FinallyStmt ; liveness=(b)",
        "      Block ; liveness=(b)",
        "        ExpressionStmt ; liveness=(b)",
        "          AssignOperation : ASSIGN ; liveness=(b)",
        "            Reference ; liveness=(b)",
        "              Identifier : d",
        "            IntegerLiteral : 1 ; liveness=(b)",
        "        Noop ; liveness=(b d)",
        "  Noop ; liveness=(b d)"
        );
  }

  public final void testTryAndCatchBreak() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a;"
            + "do {"
            + "  try {"
            + "    a = f();"  // Cannot assume this completed later
            + "    break;"
            + "  } catch (ex) {"
            + "    a = 1;"
            + "    break;"
            + "  }"
            + "} while (false);"
            + ";")),
        "Block",
        "  Declaration",
        "    Identifier : a",
        "  DoWhileLoop : ",
        "    Block",
        "      TryStmt",
        "        Block",
        "          ExpressionStmt",
        "            AssignOperation : ASSIGN",
        "              Reference",
        "                Identifier : a",
        "              SpecialOperation : FUNCTION_CALL",
        "                Reference",
        "                  Identifier : f",
        "          BreakStmt :  ; liveness=(a)",
        "        CatchStmt",
        "          Declaration",
        "            Identifier : ex",
        "          Block ; liveness=(ex@4)",
        "            ExpressionStmt ; liveness=(ex@4)",
        "              AssignOperation : ASSIGN ; liveness=(ex@4)",
        "                Reference ; liveness=(ex@4)",
        "                  Identifier : a",
        "                IntegerLiteral : 1 ; liveness=(ex@4)",
        "            BreakStmt :  ; liveness=(ex@4 a)",
        "    BooleanLiteral : false",
        "  Noop ; liveness=(a)"
        );
  }

  public final void testTryReturns() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a;"
            + "do {"
            + "  try {"
            + "    return f();"  // Whether this completes or not is irrelevant
            + "  } catch (ex) {"
            + "    a = 1;"
            // TODO(mikesamuel): should work if there is a break here.
            // To fix this, processTryStmt should be smarter.  Instead of
            // intersecting, it should recognize that if the body returns, the
            // resulting type is that of the
            + "    break;"
            + "  }"
            + "} while (false);"
            + ";")),
        "Block",
        "  Declaration",
        "    Identifier : a",
        "  DoWhileLoop : ",
        "    Block",
        "      TryStmt",
        "        Block",
        "          ReturnStmt",
        "            SpecialOperation : FUNCTION_CALL",
        "              Reference",
        "                Identifier : f",
        "        CatchStmt",
        "          Declaration",
        "            Identifier : ex",
        "          Block ; liveness=(ex@4)",
        "            ExpressionStmt ; liveness=(ex@4)",
        "              AssignOperation : ASSIGN ; liveness=(ex@4)",
        "                Reference ; liveness=(ex@4)",
        "                  Identifier : a",
        "                IntegerLiteral : 1 ; liveness=(ex@4)",
        "            BreakStmt :  ; liveness=(ex@4 a)",
        "    BooleanLiteral : false",
        "  Noop ; liveness=(a)"
        );
  }

  public final void testTryAlwaysThrows() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a;"
            + "try {"
            + "  throw new Error;\n"
            + "} catch (ex) {"
            + "  a = 1;"
            + "}"
            + ";")),
        "Block",
        "  Declaration",
        "    Identifier : a",
        "  TryStmt",
        "    Block",
        "      ThrowStmt",
        "        SpecialOperation : CONSTRUCTOR",
        "          Reference",
        "            Identifier : Error",
        "    CatchStmt",
        "      Declaration",
        "        Identifier : ex",
        "      Block ; liveness=(ex@2)",
        "        ExpressionStmt ; liveness=(ex@2)",
        "          AssignOperation : ASSIGN ; liveness=(ex@2)",
        "            Reference ; liveness=(ex@2)",
        "              Identifier : a",
        "            IntegerLiteral : 1 ; liveness=(ex@2)",
        "  Noop ; liveness=(a)"  // a is live since the catch is always reached
        );
  }

  public final void testSwitchStmtWithDefault() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b;"
            + "switch (x) {"
            + "  case 0:"
            + "    return null;"
            + "  case 1:"
            + "    if (y) { return null; }"
            + "  case (b = 2):"
            + "    a = 1;"
            + "    break;"
            + "  default:"
            + "    a = 2;"
            + "}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "  SwitchStmt : ",
        "    Reference",
        "      Identifier : x",
        "    CaseStmt",
        "      IntegerLiteral : 0",
        "      Block",
        "        ReturnStmt",
        "          NullLiteral : null",
        "    CaseStmt",
        "      IntegerLiteral : 1",
        "      Block",
        "        Conditional",
        "          Reference",
        "            Identifier : y",
        "          Block",
        "            ReturnStmt",
        "              NullLiteral : null",
        "    CaseStmt",
        "      AssignOperation : ASSIGN",
        "        Reference",
        "          Identifier : b",
        "        IntegerLiteral : 2",
        "      Block ; liveness=(b)",
        "        ExpressionStmt ; liveness=(b)",
        "          AssignOperation : ASSIGN ; liveness=(b)",
        "            Reference ; liveness=(b)",
        "              Identifier : a",
        "            IntegerLiteral : 1 ; liveness=(b)",
        "        BreakStmt :  ; liveness=(b a)",
        "    DefaultCaseStmt",
        "      Block",
        "        ExpressionStmt",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : a",
        "            IntegerLiteral : 2",
        "  Noop ; liveness=(a)");
  }

  public final void testSwitchStmtWithNonCompletingDefault() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a;"
            + "switch (s) {"
            + "  case 'a': a = false; break;"
            + "  case 'b': a = true;  break;"
            + "  default:"
            + "    return null;"
            + "}"
            + ";")),
        "Block",
        "  Declaration",
        "    Identifier : a",
        "  SwitchStmt : ",
        "    Reference",
        "      Identifier : s",
        "    CaseStmt",
        "      StringLiteral : 'a'",
        "      Block",
        "        ExpressionStmt",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : a",
        "            BooleanLiteral : false",
        "        BreakStmt :  ; liveness=(a)",
        "    CaseStmt",
        "      StringLiteral : 'b'",
        "      Block",
        "        ExpressionStmt",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : a",
        "            BooleanLiteral : true",
        "        BreakStmt :  ; liveness=(a)",
        "    DefaultCaseStmt",
        "      Block",
        "        ReturnStmt",
        "          NullLiteral : null",
        "  Noop ; liveness=(a)");
  }

  public final void testSwitchStmtWithoutDefault() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b;"
            + "switch (b = 1, x) {"
            + "  case 0:"
            + "    return null;"
            + "  case 1:"
            + "    if (y) { return null; }"
            + "  case (b = 2):"
            + "    a = 1;"
            + "    break;"
            + "}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "  SwitchStmt : ",
        "    SpecialOperation : COMMA",
        "      AssignOperation : ASSIGN",
        "        Reference",
        "          Identifier : b",
        "        IntegerLiteral : 1",
        "      Reference ; liveness=(b)",
        "        Identifier : x",
        "    CaseStmt ; liveness=(b)",
        "      IntegerLiteral : 0 ; liveness=(b)",
        "      Block ; liveness=(b)",
        "        ReturnStmt ; liveness=(b)",
        "          NullLiteral : null ; liveness=(b)",
        "    CaseStmt ; liveness=(b)",
        "      IntegerLiteral : 1 ; liveness=(b)",
        "      Block ; liveness=(b)",
        "        Conditional ; liveness=(b)",
        "          Reference ; liveness=(b)",
        "            Identifier : y",
        "          Block ; liveness=(b)",
        "            ReturnStmt ; liveness=(b)",
        "              NullLiteral : null ; liveness=(b)",
        "    CaseStmt ; liveness=(b)",
        "      AssignOperation : ASSIGN ; liveness=(b)",
        "        Reference ; liveness=(b)",
        "          Identifier : b",
        "        IntegerLiteral : 2 ; liveness=(b)",
        "      Block ; liveness=(b)",
        "        ExpressionStmt ; liveness=(b)",
        "          AssignOperation : ASSIGN ; liveness=(b)",
        "            Reference ; liveness=(b)",
        "              Identifier : a",
        "            IntegerLiteral : 1 ; liveness=(b)",
        "        BreakStmt :  ; liveness=(b a)",
        "  Noop ; liveness=(b)");
  }

  public final void testSwitchAllBreak() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b;"
            + "switch (x) {"
            + "  case 0:"
            + "    b = 1;"
            + "  case 1:"
            + "  case 2:"
            + "    a = true;"
            + "    break;"
            + "  default:"
            + "    a = false;"
            + "    break;"
            + "}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "  SwitchStmt : ",
        "    Reference",
        "      Identifier : x",
        "    CaseStmt",
        "      IntegerLiteral : 0",
        "      Block",
        "        ExpressionStmt",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : b",
        "            IntegerLiteral : 1",
        "    CaseStmt",
        "      IntegerLiteral : 1",
        "      Block",
        "    CaseStmt",
        "      IntegerLiteral : 2",
        "      Block",
        "        ExpressionStmt",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : a",
        "            BooleanLiteral : true",
        "        BreakStmt :  ; liveness=(a)",
        "    DefaultCaseStmt",
        "      Block",
        "        ExpressionStmt",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : a",
        "            BooleanLiteral : false",
        "        BreakStmt :  ; liveness=(a)",
        "  Noop ; liveness=(a)");
  }

  public final void testSwitchNoCases() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b;"
            + "switch (a = x) {}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "  SwitchStmt : ",
        "    AssignOperation : ASSIGN",
        "      Reference",
        "        Identifier : a",
        "      Reference",
        "        Identifier : x",
        "  Noop ; liveness=(a)");
  }

  public final void testFunctionBody() throws Exception {
    String inFoo = " ; liveness=(foo@2 this@2 arguments@2 x@2 y@2)";
    String inA = " ; liveness=(b@2 this@2 arguments@2)";
    String inBar = " ; liveness=(bar@2 this@2 arguments@2 x@2)";
    assertLiveness(
        js(fromString(
            ""
            + "foo(1, 2);"
            + "function foo(x, y) {"
            + "  return x + bar(y);"
            + "}"
            + "var a = function b() { return this; };"
            + "function bar(x) { return [x, x]; }"
            + ";")),
        "Block",
        "  ExpressionStmt ; liveness=(foo bar)"// fn declarations hoisted
        "    SpecialOperation : FUNCTION_CALL ; liveness=(foo bar)",
        "      Reference ; liveness=(foo bar)",
        "        Identifier : foo",
        "      IntegerLiteral : 1 ; liveness=(foo bar)",
        "      IntegerLiteral : 2 ; liveness=(foo bar)",
        "  FunctionDeclaration"// nothing live here since it was hoisted
        "    Identifier : foo",
        "    FunctionConstructor",
        "      Identifier : foo",
        "      FormalParam",
        "        Identifier : x",
        "      FormalParam",
        "        Identifier : y",
        "      Block" + inFoo,
        "        ReturnStmt" + inFoo,
        "          SimpleOperation : ADDITION" + inFoo,
        "            Reference" + inFoo,
        "              Identifier : x",
        "            SpecialOperation : FUNCTION_CALL" + inFoo,
        "              Reference" + inFoo,
        "                Identifier : bar",
        "              Reference" + inFoo,
        "                Identifier : y",
        "  Declaration ; liveness=(foo bar)"// var decls not hoisted
        "    Identifier : a",
        "    FunctionConstructor ; liveness=(foo bar)",
        "      Identifier : b",
        "      Block" + inA,
        "        ReturnStmt" + inA,
        "          Reference" + inA,
        "            Identifier : this",
        "  FunctionDeclaration ; liveness=(foo)"// Hoisted but after foo
        "    Identifier : bar",
        "    FunctionConstructor ; liveness=(foo)",
        "      Identifier : bar",
        "      FormalParam",
        "        Identifier : x",
        "      Block" + inBar,
        "        ReturnStmt" + inBar,
        "          ArrayConstructor" + inBar,
        "            Reference" + inBar,
        "              Identifier : x",
        "            Reference" + inBar,
        "              Identifier : x",
        "  Noop ; liveness=(foo bar a)"
        );
  }

  public final void testBreaksAndContinues() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b;"
            + "do {"
            + "  if (f()) {"
            + "    b = 0;"
            + "    a = g();"
            + "  } else {"
            + "    a += 1;"
            + "    break;"
            + "  }"
            + "} while (a < 10);"   // a and b are live here because of break
            + ";")),                // only a is live here
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "  DoWhileLoop : ",
        "    Block",
        "      Conditional",
        "        SpecialOperation : FUNCTION_CALL",
        "          Reference",
        "            Identifier : f",
        "        Block",
        "          ExpressionStmt",
        "            AssignOperation : ASSIGN",
        "              Reference",
        "                Identifier : b",
        "              IntegerLiteral : 0",
        "          ExpressionStmt ; liveness=(b)",
        "            AssignOperation : ASSIGN ; liveness=(b)",
        "              Reference ; liveness=(b)",
        "                Identifier : a",
        "              SpecialOperation : FUNCTION_CALL ; liveness=(b)",
        "                Reference ; liveness=(b)",
        "                  Identifier : g",
        "        Block",
        "          ExpressionStmt",
        "            AssignOperation : ASSIGN_SUM",
        "              Reference",
        "                Identifier : a",
        "              IntegerLiteral : 1",
        "          BreakStmt :  ; liveness=(a)",
        "    SimpleOperation : LESS_THAN ; liveness=(b a)",
        "      Reference ; liveness=(b a)",
        "        Identifier : a",
        "      IntegerLiteral : 10 ; liveness=(b a)",
        "  Noop ; liveness=(a)"
        );
  }

  public final void testAnonymousFunctions() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a;"
            // Assignments in immediately called closures have an effect
            + "(function () {"
            + "  a = 1;"
            + "})();"
            + ";")),
        "Block",
        "  Declaration",
        "    Identifier : a",
        "  ExpressionStmt",
        "    SpecialOperation : FUNCTION_CALL",
        "      FunctionConstructor",
        "        Identifier",
        "        Block ; liveness=(this@3 arguments@3)",
        "          ExpressionStmt ; liveness=(this@3 arguments@3)",
        "            AssignOperation : ASSIGN ; liveness=(this@3 arguments@3)",
        "              Reference ; liveness=(this@3 arguments@3)",
        "                Identifier : a",
        "              IntegerLiteral : 1 ; liveness=(this@3 arguments@3)",
        "  Noop ; liveness=(a)"
        );
  }

  public final void testWithBlock() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b, c, d = 1;"
            // Assignments inside the parentheses are safe to consider, but
            // we can't draw any conclusions about anything else.
            + "with (a = obj) {"
            + "  b = c = d;"
            + "  ;"
            + "}"
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "    Declaration",
        "      Identifier : c",
        "    Declaration",
        "      Identifier : d",
        "      IntegerLiteral : 1",
        "  WithStmt ; liveness=(d)",
        "    AssignOperation : ASSIGN ; liveness=(d)",
        "      Reference ; liveness=(d)",
        "        Identifier : a",
        "      Reference ; liveness=(d)",
        "        Identifier : obj",
        "    Block",   // No liveness info here.  Just treat it as a black hole.
        "      ExpressionStmt",
        "        AssignOperation : ASSIGN",
        "          Reference",
        "            Identifier : b",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : c",
        "            Reference",
        "              Identifier : d",
        "      Noop",
        // The assignment inside the with parens does give useful info
        "  Noop ; liveness=(d a)"
        );
  }

  public final void testUnreachableCode1() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b, c = 1;"
            + "do {"
            + "  a = 1;"
            + "  break;"
            + "  b = 1;"  // not reached
            + "} while(false);"  // not reached
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "    Declaration",
        "      Identifier : c",
        "      IntegerLiteral : 1",
        "  DoWhileLoop :  ; liveness=(c)",
        "    Block ; liveness=(c)",
        "      ExpressionStmt ; liveness=(c)",
        "        AssignOperation : ASSIGN ; liveness=(c)",
        "          Reference ; liveness=(c)",
        "            Identifier : a",
        "          IntegerLiteral : 1 ; liveness=(c)",
        "      BreakStmt :  ; liveness=(c a)",
        // No liveness calculated for unreachable code
        "      ExpressionStmt",
        "        AssignOperation : ASSIGN",
        "          Reference",
        "            Identifier : b",
        "          IntegerLiteral : 1",
        "    BooleanLiteral : false",
        "  Noop ; liveness=(c a)"
        );
  }

  public final void testUnreachableCode2() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a, b, c = 1;"
            + "do {"
            + "  a = 1;"
            + "  continue;"
            + "  b = 1;"  // Still not reached
            + "} while(false);"  // Unlike the prior, this is reached
            + ";")),
        "Block",
        "  MultiDeclaration",
        "    Declaration",
        "      Identifier : a",
        "    Declaration",
        "      Identifier : b",
        "    Declaration",
        "      Identifier : c",
        "      IntegerLiteral : 1",
        "  DoWhileLoop :  ; liveness=(c)",
        "    Block ; liveness=(c)",
        "      ExpressionStmt ; liveness=(c)",
        "        AssignOperation : ASSIGN ; liveness=(c)",
        "          Reference ; liveness=(c)",
        "            Identifier : a",
        "          IntegerLiteral : 1 ; liveness=(c)",
        "      ContinueStmt :  ; liveness=(c a)",
        // No liveness calculated for unreachable code
        "      ExpressionStmt",
        "        AssignOperation : ASSIGN",
        "          Reference",
        "            Identifier : b",
        "          IntegerLiteral : 1",
        "    BooleanLiteral : false ; liveness=(c a)",
        "  Noop ; liveness=(c a)"
        );
  }

  public final void testLoopLabels() throws Exception {
    assertLiveness(
        js(fromString(
            ""
            + "var a = null;"
            + "foo: while (1) {"
            + "  if (rand)"
            + "    break;"
            + "  else"
            + "    break foo;"
            + "  unreachable;"
            + "}"
            + ";"))// reached
        "Block",
        "  Declaration",
        "    Identifier : a",
        "    NullLiteral : null",
        "  WhileLoop : foo ; liveness=(a)",
        "    IntegerLiteral : 1 ; liveness=(a)",
        "    Block ; liveness=(a)",
        "      Conditional ; liveness=(a)",
        "        Reference ; liveness=(a)",
        "          Identifier : rand",
        "        BreakStmt :  ; liveness=(a)",
        "        BreakStmt : foo ; liveness=(a)",
        "      ExpressionStmt"// not reached
        "        Reference",
        "          Identifier : unreachable",
        "  Noop ; liveness=(a)"  // reached.  Yay!!!
        );
  }

  public final void testLogicOps() throws Exception {
    // In the next two tests,
    // a is live between the second && and the || but not right of the ||

    // This is the easy case where an assignment in the LHS of the && makes the
    // variable live in the RHS.
    assertLiveness(
        js(fromString(
            ""
            + "var a;"
            + "t && ((a = foo) && a.indexOf('f') >= 0) || f;")),
        "Block",
        "  Declaration",
        "    Identifier : a",
        "  ExpressionStmt",
        "    ControlOperation : LOGICAL_OR",
        "      ControlOperation : LOGICAL_AND",
        "        Reference",
        "          Identifier : t",
        "        ControlOperation : LOGICAL_AND",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : a",
        "            Reference",
        "              Identifier : foo",
        "          SimpleOperation : GREATER_EQUALS ; liveness=(a)",
        "            SpecialOperation : FUNCTION_CALL ; liveness=(a)",
        "              SpecialOperation : MEMBER_ACCESS ; liveness=(a)",
        "                Reference ; liveness=(a)",
        "                  Identifier : a",
        "                Reference ; liveness=(a)",
        "                  Identifier : indexOf",
        "              StringLiteral : 'f' ; liveness=(a)",
        "            IntegerLiteral : 0 ; liveness=(a)",
        "      Reference",
        "        Identifier : f");
    // But when logical ops nest, it's possible for an implementation to lose
    // liveness information.
    assertLiveness(
        js(fromString(
            ""
            + "var a;"
            + "t && (a = foo) && a.indexOf('f') >= 0 || f;")),
        "Block",
        "  Declaration",
        "    Identifier : a",
        "  ExpressionStmt",
        "    ControlOperation : LOGICAL_OR",
        "      ControlOperation : LOGICAL_AND",
        "        ControlOperation : LOGICAL_AND",
        "          Reference",
        "            Identifier : t",
        "          AssignOperation : ASSIGN",
        "            Reference",
        "              Identifier : a",
        "            Reference",
        "              Identifier : foo",
        "        SimpleOperation : GREATER_EQUALS ; liveness=(a)",
        "          SpecialOperation : FUNCTION_CALL ; liveness=(a)",
        "            SpecialOperation : MEMBER_ACCESS ; liveness=(a)",
        "              Reference ; liveness=(a)",
        "                Identifier : a",
        "              Reference ; liveness=(a)",
        "                Identifier : indexOf",
        "            StringLiteral : 'f' ; liveness=(a)",
        "          IntegerLiteral : 0 ; liveness=(a)",
        "      Reference",
        "        Identifier : f");

    assertLiveness(
        js(fromString(
            ""
            + "var a;"
            + "if (b || (a = c)) {"
            + "} else {"
            + "}"
            + ";")),
        "Block",
        "  Declaration",
        "    Identifier : a",
        "  Conditional",
        "    ControlOperation : LOGICAL_OR",
        "      Reference",
        "        Identifier : b",
        "      AssignOperation : ASSIGN",
        "        Reference",
        "          Identifier : a",
        "        Reference",
        "          Identifier : c",
        "    Block",
        "    Block ; liveness=(a)",
        "  Noop");
  }

  public final void testLiveBreaks() throws Exception {
    ExitModes exits = setup(js(fromString(
        ""
        + "a: for (var i = 0; i < 10; ++i) {\n"
        + "  if (foo()) {\n"
        + "    continue a;\n"
        + "  } else if (bar()) {\n"
        + "    break a;\n"
        + "  } else if (baz()) {\n"
        + "    break b;\n"   // LIVE line 7
        + "  }\n"
        + "}\n"
        + "switch (x) {\n"
        + "  case 0: f(); break;\n"
        + "  case 1: continue;\n"   // LIVE line 12
        + "  case 2: break b;\n"   // LIVE line 13
        + "}\n"
        + "x: for (var x in o) {\n"
        + "  break x;\n"
        + "}\n"
        + "foo: {\n"
        + "  if (Math.random()) {\n"
        + "    break foo;\n"
        + "  } else if (Math.random()) {\n"
        + "    break;\n"
        + "  } else {\n"
        + "    break bar;\n"  // LIVE 24
        + "  }\n"
        + "}\n"
        + "break;\n"   // LIVE 27
        )));
    Iterator<Statement> stmts = exits.liveExits().iterator();
    assertTrue(stmts.hasNext());
    assertEquals(
        "testLiveBreaks:7", formatShort(stmts.next().getFilePosition()));
    assertTrue(stmts.hasNext());
    assertEquals(
        "testLiveBreaks:12", formatShort(stmts.next().getFilePosition()));
    assertTrue(stmts.hasNext());
    assertEquals(
        "testLiveBreaks:13", formatShort(stmts.next().getFilePosition()));
    assertTrue(stmts.hasNext());
    assertEquals(
        "testLiveBreaks:24", formatShort(stmts.next().getFilePosition()));
    assertTrue(stmts.hasNext());
    assertEquals(
        "testLiveBreaks:27", formatShort(stmts.next().getFilePosition()));
    assertFalse(stmts.hasNext());
  }

  public final void testDirectives() throws Exception {
    assertLiveness(
        js(fromString("\"use strict\";")),
        "Block",
        "  DirectivePrologue",
        "    Directive : use strict");
  }

  public final void testDebugger() throws Exception {
    assertLiveness(
        js(fromString("debugger; var x = 3; debugger;")),
        "Block",
        "  DebuggerStmt",
        "  Declaration",
        "    Identifier : x",
        "    IntegerLiteral : 3",
        "  DebuggerStmt ; liveness=(x)");
  }

  private static ExitModes setup(ParseTreeNode js) {
    ScopeAnalyzer sa = new ScopeAnalyzer();
    sa.computeLexicalScopes(AncestorChain.instance(js));
    return VariableLiveness.calculateLiveness(js).exits;
  }

  private static void assertLiveness(ParseTreeNode js, String... golden) {
    ExitModes exits = setup(js);
    // None of the tests above have breaks or continues out of place.
    // Since the liveExits() list is used to find out of place things,
    // assert that no breaks or continues occur on the list.
    for (Statement stmt : exits.liveExits()) {
      if (!(stmt instanceof ThrowStmt || stmt instanceof ReturnStmt)) {
        fail("Unexpected exit at " + stmt.getFilePosition());
      }
    }

    StringBuilder sb = new StringBuilder();
    try {
      MessageContext mc = new MessageContext();
      mc.relevantKeys = Collections.singleton(VariableLiveness.LIVENESS);
      js.format(mc, sb);
    } catch (IOException ex) {
      throw new RuntimeException(ex)// Not for StringBuilder
    }

    MoreAsserts.assertListsEqual(
        Arrays.asList(golden),
        Arrays.asList(sb.toString().replaceAll("@0", "")
            .split("( ; liveness=\\(\\))?(?:\n|$)")));
  }
}
TOP

Related Classes of com.google.caja.ancillary.linter.VariableLivenessTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.