224 lines
5.9 KiB
Java
224 lines
5.9 KiB
Java
|
/*
|
||
|
* Copyright 2010 The Closure Compiler Authors.
|
||
|
*
|
||
|
* 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.javascript.jscomp;
|
||
|
|
||
|
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
|
||
|
import com.google.javascript.rhino.Node;
|
||
|
|
||
|
/**
|
||
|
* Tests for {@link SimpleFunctionAliasAnalysis}.
|
||
|
*
|
||
|
* @author dcc@google.com (Devin Coughlin)
|
||
|
*/
|
||
|
public class SimpleFunctionAliasAnalysisTest extends CompilerTestCase {
|
||
|
|
||
|
private SimpleFunctionAliasAnalysis analysis;
|
||
|
|
||
|
private Compiler lastCompiler;
|
||
|
|
||
|
@Override
|
||
|
protected CompilerPass getProcessor(final Compiler compiler) {
|
||
|
return new CompilerPass() {
|
||
|
|
||
|
@Override
|
||
|
public void process(Node externs, Node root) {
|
||
|
SimpleDefinitionFinder finder = new SimpleDefinitionFinder(compiler);
|
||
|
finder.process(externs, root);
|
||
|
|
||
|
analysis = new SimpleFunctionAliasAnalysis();
|
||
|
|
||
|
analysis.analyze(finder);
|
||
|
|
||
|
lastCompiler = compiler;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
public void testFunctionGetIsAliased() {
|
||
|
// Aliased by VAR assignment
|
||
|
String source =
|
||
|
"function A(){};\n" +
|
||
|
"var ns = {};\n" +
|
||
|
"ns.B = function() {};\n" +
|
||
|
"var C = function() {}\n" +
|
||
|
"var D = function() {}\n" +
|
||
|
"var aliasA = A;\n" +
|
||
|
"var aliasB = ns.B;\n" +
|
||
|
"var aliasC = C;\n" +
|
||
|
"D();";
|
||
|
|
||
|
compileAndRun(source);
|
||
|
|
||
|
assertFunctionAliased(true, "A");
|
||
|
assertFunctionAliased(true, "ns.B");
|
||
|
assertFunctionAliased(true, "C");
|
||
|
assertFunctionAliased(false, "D");
|
||
|
|
||
|
// Aliased by normal assignment
|
||
|
source =
|
||
|
"function A(){};\n" +
|
||
|
"var ns = {};\n" +
|
||
|
"ns.B = function() {};\n" +
|
||
|
"var C = function() {}\n" +
|
||
|
"ns.D = function() {}\n" +
|
||
|
"var aliasA;\n" +
|
||
|
"aliasA = A;\n" +
|
||
|
"var aliasB = {};\n" +
|
||
|
"aliasB.foo = ns.B;\n" +
|
||
|
"var aliasC;\n" +
|
||
|
"aliasC = C;\n" +
|
||
|
"ns.D();";
|
||
|
|
||
|
compileAndRun(source);
|
||
|
|
||
|
assertFunctionAliased(true, "A");
|
||
|
assertFunctionAliased(true, "ns.B");
|
||
|
assertFunctionAliased(true, "C");
|
||
|
assertFunctionAliased(false, "ns.D");
|
||
|
|
||
|
// Aliased by passing as parameter
|
||
|
source =
|
||
|
"function A(){};\n" +
|
||
|
"var ns = {};\n" +
|
||
|
"ns.B = function() {};\n" +
|
||
|
"var C = function() {}\n" +
|
||
|
"function D() {}\n" +
|
||
|
"var foo = function(a) {}\n" +
|
||
|
"foo(A);\n" +
|
||
|
"foo(ns.B)\n" +
|
||
|
"foo(C);\n" +
|
||
|
"D();";
|
||
|
|
||
|
compileAndRun(source);
|
||
|
|
||
|
assertFunctionAliased(true, "A");
|
||
|
assertFunctionAliased(true, "ns.B");
|
||
|
assertFunctionAliased(true, "C");
|
||
|
assertFunctionAliased(false, "D");
|
||
|
|
||
|
// Not aliased by being target of call
|
||
|
source =
|
||
|
"function A(){};\n" +
|
||
|
"var ns = {};\n" +
|
||
|
"ns.B = function() {};\n" +
|
||
|
"var C = function() {}\n" +
|
||
|
"A();\n" +
|
||
|
"ns.B();\n" +
|
||
|
"C();\n";
|
||
|
|
||
|
compileAndRun(source);
|
||
|
|
||
|
assertFunctionAliased(false, "A");
|
||
|
assertFunctionAliased(false, "ns.B");
|
||
|
assertFunctionAliased(false, "C");
|
||
|
|
||
|
// Not aliased by GET{PROP,ELEM}
|
||
|
source =
|
||
|
"function A(){};\n" +
|
||
|
"var ns = {};\n" +
|
||
|
"ns.B = function() {};\n" +
|
||
|
"var C = function() {}\n" +
|
||
|
"A.foo;\n" +
|
||
|
"ns.B.prototype;\n" +
|
||
|
"C[0];\n";
|
||
|
|
||
|
compileAndRun(source);
|
||
|
|
||
|
assertFunctionAliased(false, "A");
|
||
|
assertFunctionAliased(false, "ns.B");
|
||
|
assertFunctionAliased(false, "C");
|
||
|
}
|
||
|
|
||
|
public void testFunctionGetIsExposedToCallOrApply() {
|
||
|
// Exposed to call
|
||
|
String source =
|
||
|
"function A(){};\n" +
|
||
|
"function B(){};\n" +
|
||
|
"function C(){};\n" +
|
||
|
"var x;\n" +
|
||
|
"A.call(x);\n" +
|
||
|
"B.apply(x);\n" +
|
||
|
"C();\n";
|
||
|
|
||
|
compileAndRun(source);
|
||
|
|
||
|
assertFunctionExposedToCallOrApply(true, "A");
|
||
|
assertFunctionExposedToCallOrApply(true, "B");
|
||
|
assertFunctionExposedToCallOrApply(false, "C");
|
||
|
|
||
|
source =
|
||
|
"var ns = {};" +
|
||
|
"ns.A = function(){};\n" +
|
||
|
"ns.B = function(){};\n" +
|
||
|
"ns.C = function(){};\n" +
|
||
|
"var x;\n" +
|
||
|
"ns.A.call(x);\n" +
|
||
|
"ns.B.apply(x);\n" +
|
||
|
"ns.C();\n";
|
||
|
|
||
|
compileAndRun(source);
|
||
|
|
||
|
assertFunctionExposedToCallOrApply(true, "ns.A");
|
||
|
assertFunctionExposedToCallOrApply(true, "ns.B");
|
||
|
assertFunctionExposedToCallOrApply(false, "ns.C");
|
||
|
}
|
||
|
|
||
|
private void assertFunctionAliased(boolean aliasStatus,
|
||
|
String functionName) {
|
||
|
Node function = findFunction(functionName);
|
||
|
|
||
|
assertEquals(aliasStatus, analysis.isAliased(function));
|
||
|
}
|
||
|
|
||
|
private void assertFunctionExposedToCallOrApply(boolean exposure,
|
||
|
String functionName) {
|
||
|
Node function = findFunction(functionName);
|
||
|
|
||
|
assertEquals(exposure, analysis.isExposedToCallOrApply(function));
|
||
|
}
|
||
|
|
||
|
private void compileAndRun(String source) {
|
||
|
testSame(source, source, null);
|
||
|
}
|
||
|
|
||
|
private Node findFunction(String name) {
|
||
|
FunctionFinder f = new FunctionFinder(name);
|
||
|
new NodeTraversal(lastCompiler, f).traverse(lastCompiler.jsRoot);
|
||
|
assertNotNull("Couldn't find " + name, f.found);
|
||
|
return f.found;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Quick Traversal to find a given function in the AST.
|
||
|
*/
|
||
|
private class FunctionFinder extends AbstractPostOrderCallback {
|
||
|
Node found = null;
|
||
|
final String target;
|
||
|
|
||
|
FunctionFinder(String target) {
|
||
|
this.target = target;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void visit(NodeTraversal t, Node n, Node parent) {
|
||
|
if (n.isFunction()
|
||
|
&& target.equals(NodeUtil.getFunctionName(n))) {
|
||
|
found = n;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|