325 lines
11 KiB
Java
325 lines
11 KiB
Java
|
/*
|
||
|
* Copyright 2008 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.Lists;
|
||
|
import com.google.javascript.jscomp.type.FlowScope;
|
||
|
import com.google.javascript.rhino.Node;
|
||
|
import com.google.javascript.rhino.Token;
|
||
|
import com.google.javascript.rhino.jstype.JSType;
|
||
|
|
||
|
/**
|
||
|
* Tests for LinkedFlowScope.
|
||
|
* @author nicksantos@google.com (Nick Santos)
|
||
|
*/
|
||
|
public class LinkedFlowScopeTest extends CompilerTypeTestCase {
|
||
|
|
||
|
private final Node blockNode = new Node(Token.BLOCK);
|
||
|
private final Node functionNode = new Node(Token.FUNCTION);
|
||
|
private final int LONG_CHAIN_LENGTH = 1050;
|
||
|
|
||
|
private Scope globalScope;
|
||
|
private Scope localScope;
|
||
|
@SuppressWarnings("unused")
|
||
|
private FlowScope globalEntry;
|
||
|
private FlowScope localEntry;
|
||
|
|
||
|
@Override
|
||
|
public void setUp() throws Exception {
|
||
|
super.setUp();
|
||
|
|
||
|
globalScope = Scope.createGlobalScope(blockNode);
|
||
|
globalScope.declare("globalA", null, null, null);
|
||
|
globalScope.declare("globalB", null, null, null);
|
||
|
|
||
|
localScope = new Scope(globalScope, functionNode);
|
||
|
localScope.declare("localA", null, null, null);
|
||
|
localScope.declare("localB", null, null, null);
|
||
|
|
||
|
globalEntry = LinkedFlowScope.createEntryLattice(globalScope);
|
||
|
localEntry = LinkedFlowScope.createEntryLattice(localScope);
|
||
|
}
|
||
|
|
||
|
public void testOptimize() {
|
||
|
assertEquals(localEntry, localEntry.optimize());
|
||
|
|
||
|
FlowScope child = localEntry.createChildFlowScope();
|
||
|
assertEquals(localEntry, child.optimize());
|
||
|
|
||
|
child.inferSlotType("localB", NUMBER_TYPE);
|
||
|
assertEquals(child, child.optimize());
|
||
|
}
|
||
|
|
||
|
public void testJoin1() {
|
||
|
FlowScope childA = localEntry.createChildFlowScope();
|
||
|
childA.inferSlotType("localB", NUMBER_TYPE);
|
||
|
|
||
|
FlowScope childAB = childA.createChildFlowScope();
|
||
|
childAB.inferSlotType("localB", STRING_TYPE);
|
||
|
|
||
|
FlowScope childB = localEntry.createChildFlowScope();
|
||
|
childB.inferSlotType("localB", BOOLEAN_TYPE);
|
||
|
|
||
|
assertTypeEquals(STRING_TYPE, childAB.getSlot("localB").getType());
|
||
|
assertTypeEquals(BOOLEAN_TYPE, childB.getSlot("localB").getType());
|
||
|
assertNull(childB.getSlot("localA").getType());
|
||
|
|
||
|
FlowScope joined = join(childB, childAB);
|
||
|
assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE),
|
||
|
joined.getSlot("localB").getType());
|
||
|
assertNull(joined.getSlot("localA").getType());
|
||
|
|
||
|
joined = join(childAB, childB);
|
||
|
assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE),
|
||
|
joined.getSlot("localB").getType());
|
||
|
assertNull(joined.getSlot("localA").getType());
|
||
|
|
||
|
assertEquals("Join should be symmetric",
|
||
|
join(childB, childAB), join(childAB, childB));
|
||
|
}
|
||
|
|
||
|
public void testJoin2() {
|
||
|
FlowScope childA = localEntry.createChildFlowScope();
|
||
|
childA.inferSlotType("localA", STRING_TYPE);
|
||
|
|
||
|
FlowScope childB = localEntry.createChildFlowScope();
|
||
|
childB.inferSlotType("globalB", BOOLEAN_TYPE);
|
||
|
|
||
|
assertTypeEquals(STRING_TYPE, childA.getSlot("localA").getType());
|
||
|
assertTypeEquals(BOOLEAN_TYPE, childB.getSlot("globalB").getType());
|
||
|
assertNull(childB.getSlot("localB").getType());
|
||
|
|
||
|
FlowScope joined = join(childB, childA);
|
||
|
assertTypeEquals(STRING_TYPE, joined.getSlot("localA").getType());
|
||
|
assertTypeEquals(BOOLEAN_TYPE, joined.getSlot("globalB").getType());
|
||
|
|
||
|
joined = join(childA, childB);
|
||
|
assertTypeEquals(STRING_TYPE, joined.getSlot("localA").getType());
|
||
|
assertTypeEquals(BOOLEAN_TYPE, joined.getSlot("globalB").getType());
|
||
|
|
||
|
assertEquals("Join should be symmetric",
|
||
|
join(childB, childA), join(childA, childB));
|
||
|
}
|
||
|
|
||
|
public void testJoin3() {
|
||
|
localScope.declare("localC", null, STRING_TYPE, null);
|
||
|
localScope.declare("localD", null, STRING_TYPE, null);
|
||
|
|
||
|
FlowScope childA = localEntry.createChildFlowScope();
|
||
|
childA.inferSlotType("localC", NUMBER_TYPE);
|
||
|
|
||
|
FlowScope childB = localEntry.createChildFlowScope();
|
||
|
childA.inferSlotType("localD", BOOLEAN_TYPE);
|
||
|
|
||
|
FlowScope joined = join(childB, childA);
|
||
|
assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE),
|
||
|
joined.getSlot("localC").getType());
|
||
|
assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE),
|
||
|
joined.getSlot("localD").getType());
|
||
|
|
||
|
joined = join(childA, childB);
|
||
|
assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE),
|
||
|
joined.getSlot("localC").getType());
|
||
|
assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE),
|
||
|
joined.getSlot("localD").getType());
|
||
|
|
||
|
assertEquals("Join should be symmetric",
|
||
|
join(childB, childA), join(childA, childB));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a long chain of flow scopes where each link in the chain
|
||
|
* contains one slot.
|
||
|
*/
|
||
|
public void testLongChain1() {
|
||
|
FlowScope chainA = localEntry.createChildFlowScope();
|
||
|
FlowScope chainB = localEntry.createChildFlowScope();
|
||
|
for (int i = 0; i < LONG_CHAIN_LENGTH; i++) {
|
||
|
localScope.declare("local" + i, null, null, null);
|
||
|
chainA.inferSlotType("local" + i,
|
||
|
i % 2 == 0 ? NUMBER_TYPE : BOOLEAN_TYPE);
|
||
|
chainB.inferSlotType("local" + i,
|
||
|
i % 3 == 0 ? STRING_TYPE : BOOLEAN_TYPE);
|
||
|
|
||
|
chainA = chainA.createChildFlowScope();
|
||
|
chainB = chainB.createChildFlowScope();
|
||
|
}
|
||
|
|
||
|
verifyLongChains(chainA, chainB);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a long chain of flow scopes where each link in the chain
|
||
|
* contains 7 slots.
|
||
|
*/
|
||
|
public void testLongChain2() {
|
||
|
FlowScope chainA = localEntry.createChildFlowScope();
|
||
|
FlowScope chainB = localEntry.createChildFlowScope();
|
||
|
for (int i = 0; i < LONG_CHAIN_LENGTH * 7; i++) {
|
||
|
localScope.declare("local" + i, null, null, null);
|
||
|
chainA.inferSlotType("local" + i,
|
||
|
i % 2 == 0 ? NUMBER_TYPE : BOOLEAN_TYPE);
|
||
|
chainB.inferSlotType("local" + i,
|
||
|
i % 3 == 0 ? STRING_TYPE : BOOLEAN_TYPE);
|
||
|
|
||
|
if (i % 7 == 0) {
|
||
|
chainA = chainA.createChildFlowScope();
|
||
|
chainB = chainB.createChildFlowScope();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
verifyLongChains(chainA, chainB);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a long chain of flow scopes where every 4 links in the chain
|
||
|
* contain a slot.
|
||
|
*/
|
||
|
public void testLongChain3() {
|
||
|
FlowScope chainA = localEntry.createChildFlowScope();
|
||
|
FlowScope chainB = localEntry.createChildFlowScope();
|
||
|
for (int i = 0; i < LONG_CHAIN_LENGTH * 7; i++) {
|
||
|
if (i % 7 == 0) {
|
||
|
int j = i / 7;
|
||
|
localScope.declare("local" + j, null, null, null);
|
||
|
chainA.inferSlotType("local" + j,
|
||
|
j % 2 == 0 ? NUMBER_TYPE : BOOLEAN_TYPE);
|
||
|
chainB.inferSlotType("local" + j,
|
||
|
j % 3 == 0 ? STRING_TYPE : BOOLEAN_TYPE);
|
||
|
}
|
||
|
|
||
|
chainA = chainA.createChildFlowScope();
|
||
|
chainB = chainB.createChildFlowScope();
|
||
|
}
|
||
|
|
||
|
verifyLongChains(chainA, chainB);
|
||
|
}
|
||
|
|
||
|
// Common chain verification for testLongChainN for all N.
|
||
|
private void verifyLongChains(FlowScope chainA, FlowScope chainB) {
|
||
|
FlowScope joined = join(chainA, chainB);
|
||
|
for (int i = 0; i < LONG_CHAIN_LENGTH; i++) {
|
||
|
assertTypeEquals(
|
||
|
i % 2 == 0 ? NUMBER_TYPE : BOOLEAN_TYPE,
|
||
|
chainA.getSlot("local" + i).getType());
|
||
|
assertTypeEquals(
|
||
|
i % 3 == 0 ? STRING_TYPE : BOOLEAN_TYPE,
|
||
|
chainB.getSlot("local" + i).getType());
|
||
|
|
||
|
JSType joinedSlotType = joined.getSlot("local" + i).getType();
|
||
|
if (i % 6 == 0) {
|
||
|
assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE), joinedSlotType);
|
||
|
} else if (i % 2 == 0) {
|
||
|
assertTypeEquals(createUnionType(NUMBER_TYPE, BOOLEAN_TYPE),
|
||
|
joinedSlotType);
|
||
|
} else if (i % 3 == 0) {
|
||
|
assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE),
|
||
|
joinedSlotType);
|
||
|
} else {
|
||
|
assertTypeEquals(BOOLEAN_TYPE, joinedSlotType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
assertScopesDiffer(chainA, chainB);
|
||
|
assertScopesDiffer(chainA, joined);
|
||
|
assertScopesDiffer(chainB, joined);
|
||
|
}
|
||
|
|
||
|
public void testFindUniqueSlot() {
|
||
|
FlowScope childA = localEntry.createChildFlowScope();
|
||
|
childA.inferSlotType("localB", NUMBER_TYPE);
|
||
|
|
||
|
FlowScope childAB = childA.createChildFlowScope();
|
||
|
childAB.inferSlotType("localB", STRING_TYPE);
|
||
|
|
||
|
FlowScope childABC = childAB.createChildFlowScope();
|
||
|
childABC.inferSlotType("localA", BOOLEAN_TYPE);
|
||
|
|
||
|
assertNull(childABC.findUniqueRefinedSlot(childABC));
|
||
|
assertTypeEquals(BOOLEAN_TYPE,
|
||
|
childABC.findUniqueRefinedSlot(childAB).getType());
|
||
|
assertNull(childABC.findUniqueRefinedSlot(childA));
|
||
|
assertNull(childABC.findUniqueRefinedSlot(localEntry));
|
||
|
|
||
|
assertTypeEquals(STRING_TYPE,
|
||
|
childAB.findUniqueRefinedSlot(childA).getType());
|
||
|
assertTypeEquals(STRING_TYPE,
|
||
|
childAB.findUniqueRefinedSlot(localEntry).getType());
|
||
|
|
||
|
assertTypeEquals(NUMBER_TYPE,
|
||
|
childA.findUniqueRefinedSlot(localEntry).getType());
|
||
|
}
|
||
|
|
||
|
public void testDiffer1() {
|
||
|
FlowScope childA = localEntry.createChildFlowScope();
|
||
|
childA.inferSlotType("localB", NUMBER_TYPE);
|
||
|
|
||
|
FlowScope childAB = childA.createChildFlowScope();
|
||
|
childAB.inferSlotType("localB", STRING_TYPE);
|
||
|
|
||
|
FlowScope childABC = childAB.createChildFlowScope();
|
||
|
childABC.inferSlotType("localA", BOOLEAN_TYPE);
|
||
|
|
||
|
FlowScope childB = childAB.createChildFlowScope();
|
||
|
childB.inferSlotType("localB", STRING_TYPE);
|
||
|
|
||
|
FlowScope childBC = childB.createChildFlowScope();
|
||
|
childBC.inferSlotType("localA", NO_TYPE);
|
||
|
|
||
|
assertScopesSame(childAB, childB);
|
||
|
assertScopesDiffer(childABC, childBC);
|
||
|
|
||
|
assertScopesDiffer(childABC, childB);
|
||
|
assertScopesDiffer(childAB, childBC);
|
||
|
|
||
|
assertScopesDiffer(childA, childAB);
|
||
|
assertScopesDiffer(childA, childABC);
|
||
|
assertScopesDiffer(childA, childB);
|
||
|
assertScopesDiffer(childA, childBC);
|
||
|
}
|
||
|
|
||
|
public void testDiffer2() {
|
||
|
FlowScope childA = localEntry.createChildFlowScope();
|
||
|
childA.inferSlotType("localA", NUMBER_TYPE);
|
||
|
|
||
|
FlowScope childB = localEntry.createChildFlowScope();
|
||
|
childB.inferSlotType("localA", NO_TYPE);
|
||
|
|
||
|
assertScopesDiffer(childA, childB);
|
||
|
}
|
||
|
|
||
|
private void assertScopesDiffer(FlowScope a, FlowScope b) {
|
||
|
assertFalse(a.equals(b));
|
||
|
assertFalse(b.equals(a));
|
||
|
assertEquals(a, a);
|
||
|
assertEquals(b, b);
|
||
|
}
|
||
|
|
||
|
private void assertScopesSame(FlowScope a, FlowScope b) {
|
||
|
assertEquals(a, b);
|
||
|
assertEquals(b, a);
|
||
|
assertEquals(a, a);
|
||
|
assertEquals(b, b);
|
||
|
}
|
||
|
|
||
|
@SuppressWarnings("unchecked")
|
||
|
private FlowScope join(FlowScope a, FlowScope b) {
|
||
|
return (new LinkedFlowScope.FlowScopeJoinOp()).apply(
|
||
|
Lists.newArrayList(a, b));
|
||
|
}
|
||
|
}
|