Package com.google.caja.parser.quasiliteral

Source Code of com.google.caja.parser.quasiliteral.AlphaRenamingTest

// Copyright (C) 2009 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.parser.quasiliteral;

import com.google.caja.lexer.FilePosition;
import com.google.caja.parser.AncestorChain;
import com.google.caja.parser.ParseTreeNode;
import com.google.caja.parser.Visitor;
import com.google.caja.parser.js.Expression;
import com.google.caja.parser.js.FunctionConstructor;
import com.google.caja.parser.js.Identifier;
import com.google.caja.parser.js.SyntheticNodes;
import com.google.caja.reporting.MessageLevel;
import com.google.caja.reporting.MessagePart;
import com.google.caja.reporting.MessageType;
import com.google.caja.util.CajaTestCase;
import com.google.caja.util.SafeIdentifierMaker;
import com.google.caja.util.Sets;

import java.util.Arrays;
import java.util.Set;

public class AlphaRenamingTest extends CajaTestCase {
  @Override
  public void tearDown() throws Exception {
    super.tearDown();
  }

  public final void testLiteral() throws Exception {
    assertRenamed("1", "1");
    assertRenamed("1", "1", "foo", "bar");
    assertRenamed("'foo'", "'foo'", "foo", "bar");
    assertRenamed("null", "null", "foo", "bar");
    assertNoErrors();
  }

  public final void testGlobals() throws Exception {
    assertRenamed("c - a * b", "foo - bar * baz", "bar", "baz", "foo");
    assertRenamed("c - a * b", "c - a * b", "a", "b", "c");
    assertNoErrors();
  }

  public final void testFreeGlobals() throws Exception {
    assertRenamed("null", "z", "a", "b", "c");
    assertMessage(
        true, RewriterMessageType.FREE_VARIABLE, MessageLevel.ERROR,
        MessagePart.Factory.valueOf("z"));
    assertNoErrors();
  }

  public final void testPropertyNames() throws Exception {
    assertRenamed("null.bar", "foo.bar");
    assertMessage(
        true, RewriterMessageType.FREE_VARIABLE, MessageLevel.ERROR,
        MessagePart.Factory.valueOf("foo"));
    assertRenamed("a.bar", "foo.bar", "foo", "bar");
    assertRenamed("b.foo", "bar.foo", "foo", "bar");
    assertRenamed("b[a]", "bar[foo]", "foo", "bar");
    assertNoErrors();
  }

  public final void testObjects1() throws Exception {
    assertRenamed("({ x: a })", "({ x: foo })", "foo");
    assertNoErrors();
    assertRenamed("({ x: null })", "({ x: foo })");
    assertMessage(
        true, RewriterMessageType.FREE_VARIABLE, MessageLevel.ERROR,
        MessagePart.Factory.valueOf("foo"));
    assertNoErrors();
  }

  public final void testGlobalThis() throws Exception {
    assertRenamed("null.foo()", "this.foo()");
    assertMessage(
        true, RewriterMessageType.THIS_IN_GLOBAL_CONTEXT,
        MessageLevel.FATAL_ERROR);
    assertNoErrors();
  }

  public final void testGlobalArguments() throws Exception {
    assertRenamed("null.length", "arguments.length");
    assertMessage(
        true, RewriterMessageType.ARGUMENTS_IN_GLOBAL_CONTEXT,
        MessageLevel.ERROR);
    assertNoErrors();
  }

  public final void testFunctionConstructor() throws Exception {
    assertRenamed(
        ""
        // In the below, factor and unused are a and b respectively
        + "(function c(d, e) {"
        + "  var f = d + e;"
        + "  return f > 0 ? c(d - 1, e - 1) : a * f;"
        + "})",
        ""
        + "(function fn(x, y) {"
        + "  var sum = x + y;"
        + "  return sum > 0 ? fn(x - 1, y - 1) : factor * sum;"
        + "})",
        "factor", "unused");
    assertNoErrors();
  }

  public final void testFunctionDeclaration() throws Exception {
    assertRenamed(
        ""
        + "(function () {"
        + "  function b(d, e) {"
        + "    var c = b;"
        + "    return d * e - a;"
        + "  }"
        + "  return b;"
        + "})()",
        ""
        + "(function () {"
        + "  function foo(bar, baz) {"
        + "    return bar * baz - global;"
        + "  }"
        + "  return foo;"
        + "})()",
        "global");
    assertNoErrors();
  }

  public final void testRecursiveFunctionDeclaration() throws Exception {
    assertRenamed(
        ""
        + "(function () {"
        + "  function a(c) {"
        + "    var b = a;"
        + "    return c < 2 ? c : b(c - 2) + b(c - 1);"
        + "  }"
        + "})()",
        ""
        + "(function () {"
        + "  function fib(n) {"
        + "    return n < 2 ? n : fib(n - 2) + fib(n - 1);"
        + "  }"
        + "})()"
        );
    assertNoErrors();
  }

  public final void testLocalThis() throws Exception {
    assertRenamed(
        "(function () { var a = this; return a; })",
        "(function () { return this; })");
    assertNoErrors();
  }

  public final void testLocalArguments() throws Exception {
    assertRenamed(
        "(function () { var a = arguments; return a; })",
        "(function () { return arguments; })");
    assertNoErrors();
  }

  public final void testCatch() throws Exception {
    assertRenamed(
        "(function (a) { try {} catch (b) { throw b; } return a; })",
        "(function (e) { try {} catch (e) { throw e; } return e; })");
    assertMessage(
        true, MessageType.MASKING_SYMBOL, MessageLevel.LINT,
        MessagePart.Factory.valueOf("e"));
    assertNoErrors();
  }

  public final void testMaskingFunctionDeclarations() throws Exception {
    assertRenamed(
        ""
        + "(function () {"
        + "  function b() {"
        // There cannot be a {var c = b} here since the hoisted function
        // declaration should dominate it.
        + "    function c() {"
        + "      var d = c;"
        + "      return d, a;"
        + "    }"
        + "    return c;"
        + "  }"
        + "  return b;"
        + "})",
        ""
        + "(function () {"
        + "  function f() {"
        + "    function f() {"
        + "      return f, global;"
        + "    }"
        + "    return f;"
        + "  }"
        + "  return f;"
        + "})",
        "global");
    assertMessage(
        true, MessageType.SYMBOL_REDEFINED, MessageLevel.LINT,
        FilePosition.instance(is, 1, 44, 44, 1),
        FilePosition.instance(is, 1, 26, 26, 1),
        MessagePart.Factory.valueOf("f"));
    assertNoErrors();
  }

  public final void testMaskingFunctionConstructors() throws Exception {
    assertRenamed(
        "(function b() { return b, function c() { return c, a; }; })",
        "(function f() { return f, function f() { return f, global; }; })",
        "global");
    assertNoErrors();
  }

  public final void testMaskingGlobals() throws Exception {
    assertRenamed(
        "(function(){var b=(function(){var c=2;return c;})();return b;})()",
        "(function(){var x=(function(){var x=2;return x;})();return x;})()",
        "x");
    assertNoErrors();
  }

  public final void testMaskingLocals() throws Exception {
    assertRenamed(
        "a + (function () { var b = 2; return b; })()",
        "x + (function () { var x = 2; return x; })()",
        "x");
    assertNoErrors();
  }

  public final void testMaskingFormals() throws Exception {
    assertRenamed(
        ""
        + "a + (function (b) {"
        + "       function c(e) { var d = c; return e * e; }"
        + "       return c(b);"
        + "     })()",
        "x + (function (x) { function f(x) { return x * x; } return f(x); })()",
        "x");
    assertNoErrors();
  }

  public final void testVarArguments() throws Exception {
    assertRenamed(
        ""
        + "(function () {"
        // This matches the behavior of all major interpreters except Opera.
        + "  var a = arguments;"
        + "  var a = a;"
        + "  return a;"
        + "})",
        ""
        + "(function () {"
        + "  var arguments = arguments;"
        + "  return arguments;"
        + "})");
    assertMessage(
        true, RewriterMessageType.CANNOT_MASK_IDENTIFIER,
        MessageLevel.FATAL_ERROR, MessagePart.Factory.valueOf("arguments"));
    assertNoErrors();
  }

  public final void testVarMaskingFunctionSelfName() throws Exception {
    assertRenamed(
        "(function b() { var b = b; return b; })",
        "(function f() { var f = f; return f; })", "f");
    assertMessage(true, MessageType.SYMBOL_REDEFINED, MessageLevel.LINT,
                  MessagePart.Factory.valueOf("f"));
    assertNoErrors();
  }

  public final void testMultiDeclaration() throws Exception {
    assertRenamed(
        "(function (a, b, c) { var c, d, e, d; return a + b + c + d + e; })",
        "(function (m, n, o) { var o, p, q, p; return m + n + o + p + q; })");
    assertNoErrors();
  }

  public final void testSynthetics() throws Exception {
    assertRenamed(
        ""
        + "(function foo___(b, y___, d) {"
        + "  var w___ = d * d;"
        + "  function inner___() {}"
        + "  return b + y___ * w___;"
        + "})",
        ""
        + "(function foo___(x, y___, z) {"
        + "  var w___ = z * z;"
        + "  function inner___() {}"
        + "  return x + y___ * w___;"
        + "})");
  }

  public final void testSanityChecks() throws Exception {
    // ___ is free but synthetic so that it cannot just be rewritten.
    assertRenamed("null", "___.foo()");
    assertMessage(
        true, RewriterMessageType.ALPHA_RENAMING_FAILURE, MessageLevel.ERROR,
        MessagePart.Factory.valueOf(
            Arrays.asList(MessagePart.Factory.valueOf("___"))));
    // If ___ is provided as a global, it is allowed.
    assertRenamed("___.foo()", "___.foo()", "___");
    assertNoErrors();
  }

  public final void testRenamingOfPseudoKeywords() throws Exception {
    assertRenamed(
        ""
        + "[function (a) { var a = arguments; return a; },"
        + " function () { var b = arguments; return b; }]",
        ""
        + "[function (arguments) { return arguments; },"
        + " function () { return arguments; }]");
    assertRenamed(
        ""
        + "[function (b) { return b; },"
        + " function () { return a; }]",
        ""
        + "[function (undefined) { return undefined; },"
        + " function () { return undefined; }]",
        "undefined");
    assertMessage(
        true, MessageType.DUPLICATE_FORMAL_PARAM, MessageLevel.ERROR,
        MessagePart.Factory.valueOf("arguments"));
    assertMessage(
        true, RewriterMessageType.CANNOT_MASK_IDENTIFIER,
        MessageLevel.FATAL_ERROR, MessagePart.Factory.valueOf("arguments"));
    assertNoErrors();
  }

  public final void testDuplicateFormals() throws Exception {
    assertRenamed(
        "function (a, b, a) { return a - b; }",
        "function (x, y, x) { return x - y; }");
    assertMessage(
        true, MessageType.DUPLICATE_FORMAL_PARAM, MessageLevel.ERROR,
        MessagePart.Factory.valueOf("x"));
    // From the sanity check.
    assertMessage(
        true, MessageType.DUPLICATE_FORMAL_PARAM, MessageLevel.ERROR,
        MessagePart.Factory.valueOf("a"));
    assertNoErrors();
  }

  private void assertRenamed(String golden, String input, String... globals)
      throws Exception {
    NameContext<String, ?> nc = new NameContext<String, Object>(
        new SafeIdentifierMaker());
    Set<String> freeSynthetics = Sets.newLinkedHashSet();
    for (String global : globals) {
      if (global.endsWith("__")) {
        freeSynthetics.add(global);
      } else {
        nc.declare(global, FilePosition.startOfFile(is));
      }
    }
    Expression renamed = AlphaRenaming.rename(
        synth(jsExpr(fromString(input))), nc, freeSynthetics, mq);
    assertEquals(render(jsExpr(fromString(golden))), render(renamed));
  }

  private static Expression synth(Expression e) {
    e.acceptPreOrder(new Visitor() {
      public boolean visit(AncestorChain<?> chain) {
        ParseTreeNode n = chain.node;
        if (n instanceof Identifier) {
          Identifier id = (Identifier) n;
          if (id.getName() != null && id.getName().endsWith("___")) {
            SyntheticNodes.s(id);
          }
        } else if (n instanceof FunctionConstructor) {
          FunctionConstructor fc = (FunctionConstructor) n;
          if (fc.getIdentifierName() != null
              && fc.getIdentifierName().endsWith("___")) {
            SyntheticNodes.s(fc);
          }
        }
        return true;
      }
    }, null);
    return e;
  }
}
TOP

Related Classes of com.google.caja.parser.quasiliteral.AlphaRenamingTest

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.