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/MarkNoSideEffectCallsTest.java
github-classroom[bot] e42e547e48
Initial commit
2023-04-25 11:33:41 +00:00

304 lines
11 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.ImmutableList;
import com.google.common.collect.Lists;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import static com.google.javascript.jscomp.MarkNoSideEffectCalls.INVALID_NO_SIDE_EFFECT_ANNOTATION;
import com.google.javascript.rhino.Node;
import java.util.Collections;
import java.util.List;
/**
* Tests for {@link MarkNoSideEffectCalls}
*
*/
public class MarkNoSideEffectCallsTest extends CompilerTestCase {
List<String> noSideEffectCalls = Lists.newArrayList();
private static String kExterns =
"function externSef1(){}" +
"/**@nosideeffects*/function externNsef1(){}" +
"var externSef2 = function(){};" +
"/**@nosideeffects*/var externNsef2 = function(){};" +
"var externNsef3 = /**@nosideeffects*/function(){};" +
"var externObj;" +
"externObj.sef1 = function(){};" +
"/**@nosideeffects*/externObj.nsef1 = function(){};" +
"externObj.nsef2 = /**@nosideeffects*/function(){};" +
"externObj.sef2;" +
"/**@nosideeffects*/externObj.nsef3;";
public MarkNoSideEffectCallsTest() {
super(kExterns);
}
@Override
protected int getNumRepetitions() {
// run pass once.
return 1;
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
noSideEffectCalls.clear();
}
public void testFunctionAnnotation() throws Exception {
testMarkCalls("/**@nosideeffects*/function f(){}", "f()",
ImmutableList.of("f"));
testMarkCalls("/**@nosideeffects*/var f = function(){};", "f()",
ImmutableList.of("f"));
testMarkCalls("var f = /**@nosideeffects*/function(){};", "f()",
ImmutableList.of("f"));
testMarkCalls("var f; f = /**@nosideeffects*/function(){};", "f()",
ImmutableList.of("f"));
testMarkCalls("var f; /**@nosideeffects*/ f = function(){};", "f()",
ImmutableList.of("f"));
// no annotation
testMarkCalls("function f(){}", Collections.<String>emptyList());
testMarkCalls("function f(){} f()", Collections.<String>emptyList());
// 2 annotations
testMarkCalls("/**@nosideeffects*/var f = " +
"/**@nosideeffects*/function(){};",
"f()",
ImmutableList.of("f"));
}
public void testNamespaceAnnotation() throws Exception {
testMarkCalls("var o = {}; o.f = /**@nosideeffects*/function(){};",
"o.f()", ImmutableList.of("o.f"));
testMarkCalls("var o = {}; o.f = /**@nosideeffects*/function(){};",
"o.f()", ImmutableList.of("o.f"));
testMarkCalls("var o = {}; o.f = function(){}; o.f()",
Collections.<String>emptyList());
}
public void testConstructorAnnotation() throws Exception {
testMarkCalls("/**@nosideeffects*/function c(){};", "new c",
ImmutableList.of("c"));
testMarkCalls("var c = /**@nosideeffects*/function(){};", "new c",
ImmutableList.of("c"));
testMarkCalls("/**@nosideeffects*/var c = function(){};", "new c",
ImmutableList.of("c"));
testMarkCalls("function c(){}; new c", Collections.<String>emptyList());
}
public void testMultipleDefinition() throws Exception {
testMarkCalls("/**@nosideeffects*/function f(){}" +
"/**@nosideeffects*/f = function(){};",
"f()",
ImmutableList.of("f"));
testMarkCalls("function f(){}" +
"/**@nosideeffects*/f = function(){};",
"f()",
Collections.<String>emptyList());
testMarkCalls("/**@nosideeffects*/function f(){}",
"f = function(){};" +
"f()",
Collections.<String>emptyList());
}
public void testAssignNoFunction() throws Exception {
testMarkCalls("/**@nosideeffects*/function f(){}", "f = 1; f()",
ImmutableList.of("f"));
testMarkCalls("/**@nosideeffects*/function f(){}", "f = 1 || 2; f()",
Collections.<String>emptyList());
}
public void testPrototype() throws Exception {
testMarkCalls("function c(){};" +
"/**@nosideeffects*/c.prototype.g = function(){};",
"var o = new c; o.g()",
ImmutableList.of("o.g"));
testMarkCalls("function c(){};" +
"/**@nosideeffects*/c.prototype.g = function(){};",
"function f(){}" +
"var o = new c; o.g(); f()",
ImmutableList.of("o.g"));
// replace o.f with a function that has side effects
testMarkCalls("function c(){};" +
"/**@nosideeffects*/c.prototype.g = function(){};",
"var o = new c;" +
"o.g = function(){};" +
"o.g()",
ImmutableList.<String>of());
// two classes with same property; neither has side effects
testMarkCalls("function c1(){};" +
"/**@nosideeffects*/c1.prototype.f = function(){};" +
"function c2(){};" +
"/**@nosideeffects*/c2.prototype.f = function(){};",
"var o = new c1;" +
"o.f()",
ImmutableList.of("o.f"));
// two classes with same property; one has side effects
testMarkCalls("function c1(){};" +
"/**@nosideeffects*/c1.prototype.f = function(){};",
"function c2(){};" +
"c2.prototype.f = function(){};" +
"var o = new c1;" +
"o.f()",
Collections.<String>emptyList());
}
public void testAnnotationInExterns() throws Exception {
testMarkCalls("externSef1()", Collections.<String>emptyList());
testMarkCalls("externSef2()", Collections.<String>emptyList());
testMarkCalls("externNsef1()", ImmutableList.of("externNsef1"));
testMarkCalls("externNsef2()", ImmutableList.of("externNsef2"));
testMarkCalls("externNsef3()", ImmutableList.of("externNsef3"));
}
public void testNamespaceAnnotationInExterns() throws Exception {
testMarkCalls("externObj.sef1()", Collections.<String>emptyList());
testMarkCalls("externObj.sef2()", Collections.<String>emptyList());
testMarkCalls("externObj.nsef1()", ImmutableList.of("externObj.nsef1"));
testMarkCalls("externObj.nsef2()", ImmutableList.of("externObj.nsef2"));
testMarkCalls("externObj.nsef3()", ImmutableList.of("externObj.nsef3"));
}
public void testOverrideDefinitionInSource() throws Exception {
// both have side effects.
testMarkCalls("var obj = {}; obj.sef1 = function(){}; obj.sef1()",
Collections.<String>emptyList());
// extern has side effects.
testMarkCalls("var obj = {};" +
"/**@nosideeffects*/obj.sef1 = function(){};",
"obj.sef1()",
Collections.<String>emptyList());
// override in source also has side effects.
testMarkCalls("var obj = {}; obj.nsef1 = function(){}; obj.nsef1()",
Collections.<String>emptyList());
// override in source also has no side effects.
testMarkCalls("var obj = {};" +
"/**@nosideeffects*/obj.nsef1 = function(){};",
"obj.nsef1()",
ImmutableList.of("obj.nsef1"));
}
public void testApply1() throws Exception {
testMarkCalls("/**@nosideeffects*/ var f = function() {}",
"f.apply()",
ImmutableList.of("f.apply"));
}
public void testApply2() throws Exception {
testMarkCalls("var f = function() {}",
"f.apply()",
ImmutableList.<String>of());
}
public void testCall1() throws Exception {
testMarkCalls("/**@nosideeffects*/ var f = function() {}",
"f.call()",
ImmutableList.of("f.call"));
}
public void testCall2() throws Exception {
testMarkCalls("var f = function() {}",
"f.call()",
ImmutableList.<String>of());
}
public void testInvalidAnnotation1() throws Exception {
test("/** @nosideeffects */ function foo() {}",
null, INVALID_NO_SIDE_EFFECT_ANNOTATION);
}
public void testInvalidAnnotation2() throws Exception {
test("var f = /** @nosideeffects */ function() {}",
null, INVALID_NO_SIDE_EFFECT_ANNOTATION);
}
public void testInvalidAnnotation3() throws Exception {
test("/** @nosideeffects */ var f = function() {}",
null, INVALID_NO_SIDE_EFFECT_ANNOTATION);
}
public void testInvalidAnnotation4() throws Exception {
test("var f = function() {};" +
"/** @nosideeffects */ f.x = function() {}",
null, INVALID_NO_SIDE_EFFECT_ANNOTATION);
}
public void testInvalidAnnotation5() throws Exception {
test("var f = function() {};" +
"f.x = /** @nosideeffects */ function() {}",
null, INVALID_NO_SIDE_EFFECT_ANNOTATION);
}
void testMarkCalls(String source, List<String> expected) {
testMarkCalls("", source, expected);
}
void testMarkCalls(
String extraExterns, String source, List<String> expected) {
testSame(kExterns + extraExterns, source, null);
assertEquals(expected, noSideEffectCalls);
noSideEffectCalls.clear();
}
@Override
protected CompilerPass getProcessor(Compiler compiler) {
return new NoSideEffectCallEnumerator(compiler);
}
/**
* Run MarkNoSideEffectCalls, then gather a list of calls that are
* marked as having no side effects.
*/
private class NoSideEffectCallEnumerator
extends AbstractPostOrderCallback implements CompilerPass {
private final MarkNoSideEffectCalls passUnderTest;
private final Compiler compiler;
NoSideEffectCallEnumerator(Compiler compiler) {
this.passUnderTest = new MarkNoSideEffectCalls(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);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isNew()) {
if (!NodeUtil.constructorCallHasSideEffects(n)) {
noSideEffectCalls.add(n.getFirstChild().getQualifiedName());
}
} else if (n.isCall()) {
if (!NodeUtil.functionCallHasSideEffects(n)) {
noSideEffectCalls.add(n.getFirstChild().getQualifiedName());
}
}
}
}
}