This repository has been archived on 2023-06-18. You can view files and clone it, but cannot push or open issues or pull requests.
ima02/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/SimpleDefinitionFinderTest.java
github-classroom[bot] e42e547e48
Initial commit
2023-04-25 11:33:41 +00:00

415 lines
13 KiB
Java

/*
* Copyright 2009 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.common.collect.ImmutableSet;
import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeMultiset;
import com.google.javascript.jscomp.DefinitionsRemover.Definition;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Collection;
import java.util.Set;
/**
* Tests for {@link SimpleDefinitionFinder}
*
*/
public class SimpleDefinitionFinderTest extends CompilerTestCase {
Set<String> found = Sets.newTreeSet();
@Override
protected int getNumRepetitions() {
// run pass once.
return 1;
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
found.clear();
}
public void testDefineNumber() throws Exception {
checkDefinitionsInJs(
"var a = 1",
ImmutableSet.of("DEF NAME a -> NUMBER"));
checkDefinitionsInJs(
"a = 1",
ImmutableSet.of("DEF NAME a -> NUMBER"));
checkDefinitionsInJs(
"a.b = 1",
ImmutableSet.of("DEF GETPROP a.b -> NUMBER"));
// getelem expressions are invisible to the definition gatherer.
checkDefinitionsInJs(
"a[\"b\"] = 1",
ImmutableSet.<String>of());
checkDefinitionsInJs(
"f().b = 1",
ImmutableSet.of("DEF GETPROP null -> NUMBER"));
checkDefinitionsInJs(
"({a : 1}); o.a",
ImmutableSet.of("DEF STRING_KEY null -> NUMBER",
"USE GETPROP o.a -> [NUMBER]"));
// TODO(johnlenz): Fix this.
checkDefinitionsInJs(
"({'a' : 1}); o['a']",
ImmutableSet.<String>of("DEF STRING_KEY null -> NUMBER"));
checkDefinitionsInJs(
"({1 : 1}); o[1]",
ImmutableSet.<String>of("DEF STRING_KEY null -> NUMBER"));
checkDefinitionsInJs(
"var a = {b : 1}; a.b",
ImmutableSet.of("DEF NAME a -> <null>",
"DEF STRING_KEY null -> NUMBER",
"USE NAME a -> [<null>]",
"USE GETPROP a.b -> [NUMBER]"));
}
public void testDefineGet() throws Exception {
// TODO(johnlenz): Add support for quoted properties
checkDefinitionsInJs(
"({get a() {}}); o.a",
ImmutableSet.of("DEF GETTER_DEF null -> FUNCTION",
"USE GETPROP o.a -> [FUNCTION]"));
}
public void testDefineSet() throws Exception {
// TODO(johnlenz): Add support for quoted properties
checkDefinitionsInJs(
"({set a(b) {}}); o.a",
ImmutableSet.of("DEF NAME b -> <null>",
"DEF SETTER_DEF null -> FUNCTION",
"USE GETPROP o.a -> [FUNCTION]"));
}
public void testDefineFunction() throws Exception {
checkDefinitionsInJs(
"var a = function(){}",
ImmutableSet.of("DEF NAME a -> FUNCTION"));
checkDefinitionsInJs(
"var a = function f(){}",
ImmutableSet.of("DEF NAME f -> FUNCTION", "DEF NAME a -> FUNCTION"));
checkDefinitionsInJs(
"function a(){}",
ImmutableSet.of("DEF NAME a -> FUNCTION"));
checkDefinitionsInJs(
"a = function(){}",
ImmutableSet.of("DEF NAME a -> FUNCTION"));
checkDefinitionsInJs(
"a.b = function(){}",
ImmutableSet.of("DEF GETPROP a.b -> FUNCTION"));
// getelem expressions are invisible to the definition gatherer.
checkDefinitionsInJs(
"a[\"b\"] = function(){}",
ImmutableSet.<String>of());
checkDefinitionsInJs(
"f().b = function(){}",
ImmutableSet.of("DEF GETPROP null -> FUNCTION"));
}
public void testFunctionArgumentsBasic() throws Exception {
checkDefinitionsInJs(
"function f(a){return a}",
ImmutableSet.of("DEF NAME a -> <null>",
"USE NAME a -> [<null>]",
"DEF NAME f -> FUNCTION"));
checkDefinitionsInJs(
"var a = 1; function f(a){return a}",
ImmutableSet.of("DEF NAME a -> NUMBER",
"DEF NAME a -> <null>",
"USE NAME a -> [<null>, NUMBER]",
"DEF NAME f -> FUNCTION"));
}
public void testFunctionArgumentsInExterns() throws Exception {
final String DEF = "var f = function(arg1, arg2){}";
final String USE = "f(1, 2)";
// function arguments are definitions when they appear in source.
checkDefinitionsInJs(
DEF + ";" + USE,
ImmutableSet.of("DEF NAME f -> FUNCTION",
"DEF NAME arg1 -> <null>",
"DEF NAME arg2 -> <null>",
"USE NAME f -> [FUNCTION]"));
// function arguments are NOT definitions when they appear in externs.
checkDefinitions(
DEF, USE,
ImmutableSet.of("DEF NAME f -> EXTERN FUNCTION",
"USE NAME f -> [EXTERN FUNCTION]"));
}
public void testMultipleDefinition() throws Exception {
checkDefinitionsInJs(
"a = 1; a = 2; a",
ImmutableSet.of("DEF NAME a -> NUMBER",
"USE NAME a -> [NUMBER x 2]"));
checkDefinitionsInJs(
"a = 1; a = 'a'; a",
ImmutableSet.of("DEF NAME a -> NUMBER",
"DEF NAME a -> STRING",
"USE NAME a -> [NUMBER, STRING]"));
checkDefinitionsInJs(
"a = 1; b = 2; a = b; a",
ImmutableSet.of("DEF NAME a -> <null>",
"DEF NAME a -> NUMBER",
"DEF NAME b -> NUMBER",
"USE NAME a -> [<null>, NUMBER]",
"USE NAME b -> [NUMBER]"));
checkDefinitionsInJs(
"a = 1; b = 2; c = b; c = a; c",
ImmutableSet.of("DEF NAME a -> NUMBER",
"DEF NAME b -> NUMBER",
"DEF NAME c -> <null>",
"USE NAME a -> [NUMBER]",
"USE NAME b -> [NUMBER]",
"USE NAME c -> [<null> x 2]"));
checkDefinitionsInJs(
"function f(){} f()",
ImmutableSet.of("DEF NAME f -> FUNCTION",
"USE NAME f -> [FUNCTION]"));
checkDefinitionsInJs(
"function f(){} f.call(null)",
ImmutableSet.of("DEF NAME f -> FUNCTION",
"USE NAME f -> [FUNCTION]",
"USE GETPROP f.call -> [FUNCTION]"));
checkDefinitionsInJs(
"function f(){} f.apply(null, [])",
ImmutableSet.of("DEF NAME f -> FUNCTION",
"USE NAME f -> [FUNCTION]",
"USE GETPROP f.apply -> [FUNCTION]"));
checkDefinitionsInJs(
"function f(){} f.foobar()",
ImmutableSet.of("DEF NAME f -> FUNCTION",
"USE NAME f -> [FUNCTION]"));
checkDefinitionsInJs(
"function f(){} f(); f.call(null)",
ImmutableSet.of("DEF NAME f -> FUNCTION",
"USE NAME f -> [FUNCTION]",
"USE GETPROP f.call -> [FUNCTION]"));
}
public void testDefinitionInExterns() throws Exception {
String externs = "var a = 1";
checkDefinitionsInExterns(
externs,
ImmutableSet.of("DEF NAME a -> EXTERN NUMBER"));
checkDefinitions(
externs,
"var b = 1",
ImmutableSet.of("DEF NAME a -> EXTERN NUMBER", "DEF NAME b -> NUMBER"));
checkDefinitions(
externs,
"a = \"foo\"; a",
ImmutableSet.of("DEF NAME a -> EXTERN NUMBER",
"DEF NAME a -> STRING",
"USE NAME a -> [EXTERN NUMBER, STRING]"));
checkDefinitionsInExterns(
"var a = {}; a.b = 10",
ImmutableSet.of("DEF GETPROP a.b -> EXTERN NUMBER",
"DEF NAME a -> EXTERN <null>",
"USE NAME a -> [EXTERN <null>]"));
checkDefinitionsInExterns(
"var a = {}; a.b",
ImmutableSet.of("DEF GETPROP a.b -> EXTERN <null>",
"DEF NAME a -> EXTERN <null>",
"USE NAME a -> [EXTERN <null>]"));
checkDefinitions(
"var a = {}",
"a.b = 1",
ImmutableSet.of("DEF GETPROP a.b -> NUMBER",
"DEF NAME a -> EXTERN <null>",
"USE NAME a -> [EXTERN <null>]"));
checkDefinitions(
"var a = {}",
"a.b",
ImmutableSet.of("DEF NAME a -> EXTERN <null>",
"USE NAME a -> [EXTERN <null>]"));
checkDefinitionsInExterns(
externs,
ImmutableSet.of("DEF NAME a -> EXTERN NUMBER"));
}
public void testObjectLitInExterns() {
checkDefinitions(
"var goog = {};" +
"/** @type {number} */ goog.HYBRID;" +
"/** @enum */ goog.Enum = {HYBRID: 0, ROADMAP: 1};",
"goog.HYBRID; goog.Enum.ROADMAP;",
ImmutableSet.of(
"DEF GETPROP goog.Enum -> EXTERN <null>",
"DEF GETPROP goog.HYBRID -> EXTERN <null>",
"DEF NAME goog -> EXTERN <null>",
"DEF STRING_KEY null -> EXTERN NUMBER",
"USE GETPROP goog.Enum -> [EXTERN <null>]",
"USE GETPROP goog.Enum.ROADMAP -> [EXTERN NUMBER]",
"USE GETPROP goog.HYBRID -> [EXTERN <null>, EXTERN NUMBER]",
"USE NAME goog -> [EXTERN <null>]"));
}
public void testCallInExterns() {
checkDefinitionsInExterns(
"var goog = {};" +
"/** @constructor */ goog.Response = function() {};" +
"goog.Response.prototype.get;" +
"goog.Response.prototype.get().get;",
ImmutableSet.of(
"DEF GETPROP goog.Response -> EXTERN FUNCTION",
"DEF GETPROP goog.Response.prototype.get -> EXTERN <null>",
"DEF GETPROP null -> EXTERN <null>",
"DEF NAME goog -> EXTERN <null>",
"USE GETPROP goog.Response -> [EXTERN FUNCTION]",
"USE GETPROP goog.Response.prototype.get -> [EXTERN <null> x 2]",
"USE NAME goog -> [EXTERN <null>]"));
}
void checkDefinitionsInExterns(String externs, Set<String> expected) {
checkDefinitions(externs, "", expected);
}
void checkDefinitionsInJs(String js, Set<String> expected) {
checkDefinitions("", js, expected);
}
void checkDefinitions(String externs, String source, Set<String> expected) {
testSame(externs, source, null);
assertEquals(expected, found);
found.clear();
}
@Override
protected CompilerPass getProcessor(Compiler compiler) {
return new SimpleDefinitionEnumerator(compiler);
}
/**
* Run SimpleDefinitionFinder, then gather a list of definitions.
*/
private class SimpleDefinitionEnumerator
extends AbstractPostOrderCallback implements CompilerPass {
private final SimpleDefinitionFinder passUnderTest;
private final Compiler compiler;
SimpleDefinitionEnumerator(Compiler compiler) {
this.passUnderTest = new SimpleDefinitionFinder(compiler);
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
passUnderTest.process(externs, root);
NodeTraversal.traverse(compiler, externs, this);
NodeTraversal.traverse(compiler, root, this);
for (DefinitionSite defSite : passUnderTest.getDefinitionSites()) {
Node node = defSite.node;
Definition definition = defSite.definition;
StringBuilder sb = new StringBuilder();
sb.append("DEF ");
sb.append(Token.name(node.getType()));
sb.append(" ");
sb.append(node.getQualifiedName());
sb.append(" -> ");
if (definition.isExtern()) {
sb.append("EXTERN ");
}
Node rValue = definition.getRValue();
if (rValue != null) {
sb.append(Token.name(rValue.getType()));
} else {
sb.append("<null>");
}
found.add(sb.toString());
}
}
@Override
public void visit(NodeTraversal traversal, Node node, Node parent) {
Collection<Definition> defs =
passUnderTest.getDefinitionsReferencedAt(node);
if (defs != null) {
StringBuilder sb = new StringBuilder();
sb.append("USE ");
sb.append(Token.name(node.getType()));
sb.append(" ");
sb.append(node.getQualifiedName());
sb.append(" -> ");
Multiset<String> defstrs = TreeMultiset.create();
for (Definition def : defs) {
String defstr;
Node rValue = def.getRValue();
if (rValue != null) {
defstr = Token.name(rValue.getType());
} else {
defstr = "<null>";
}
if (def.isExtern()) {
defstr = "EXTERN " + defstr;
}
defstrs.add(defstr);
}
sb.append(defstrs.toString());
found.add(sb.toString());
}
}
}
}