/* * Copyright 2017 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 static com.google.common.truth.Truth.assertThat; import static com.google.javascript.jscomp.CompilerOptions.LanguageMode.ECMASCRIPT_NEXT; import static com.google.javascript.jscomp.testing.NodeSubject.assertNode; import com.google.common.collect.ImmutableMap; import com.google.javascript.rhino.Token; public final class CrossModuleReferenceCollectorTest extends CompilerTestCase { private CrossModuleReferenceCollector testedCollector; @Override public void setUp() { setLanguage(ECMASCRIPT_NEXT, ECMASCRIPT_NEXT); } @Override protected int getNumRepetitions() { // Default behavior for CompilerTestCase.test*() methods is to do the whole test twice, // because passes that modify the AST need to be idempotent. // Since CrossModuleReferenceCollector() just gathers information, it doesn't make sense to // run it twice, and doing so just complicates debugging test cases. return 1; } @Override protected CompilerPass getProcessor(final Compiler compiler) { ScopeCreator scopeCreator = new Es6SyntacticScopeCreator(compiler); testedCollector = new CrossModuleReferenceCollector( compiler, scopeCreator); return testedCollector; } public void testVarInBlock() { testSame(LINE_JOINER.join( " if (true) {", " var y = x;", " y;", " y;", " }")); ImmutableMap<String, Var> globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); assertThat(globalVariableNamesMap).containsKey("y"); Var yVar = globalVariableNamesMap.get("y"); ReferenceCollection yRefs = testedCollector.getReferences(yVar); assertThat(yRefs.isAssignedOnceInLifetime()).isTrue(); assertThat(yRefs.isWellDefined()).isTrue(); } public void testVarInLoopNotAssignedOnlyOnceInLifetime() { testSame("var x; while (true) { x = 0; }"); ImmutableMap<String, Var> globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); Var xVar = globalVariableNamesMap.get("x"); assertThat(globalVariableNamesMap).containsKey("x"); ReferenceCollection xRefs = testedCollector.getReferences(xVar); assertThat(xRefs.isAssignedOnceInLifetime()).isFalse(); testSame("let x; while (true) { x = 0; }"); globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); xVar = globalVariableNamesMap.get("x"); assertThat(globalVariableNamesMap).containsKey("x"); xRefs = testedCollector.getReferences(xVar); assertThat(xRefs.isAssignedOnceInLifetime()).isFalse(); } /** * Although there is only one assignment to x in the code, it's in a function which could be * called multiple times, so {@code isAssignedOnceInLifetime()} returns false. */ public void testVarInFunctionNotAssignedOnlyOnceInLifetime() { testSame("var x; function f() { x = 0; }"); ImmutableMap<String, Var> globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); Var xVar = globalVariableNamesMap.get("x"); assertThat(globalVariableNamesMap).containsKey("x"); ReferenceCollection xRefs = testedCollector.getReferences(xVar); assertThat(xRefs.isAssignedOnceInLifetime()).isFalse(); testSame("let x; function f() { x = 0; }"); globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); xVar = globalVariableNamesMap.get("x"); assertThat(globalVariableNamesMap).containsKey("x"); xRefs = testedCollector.getReferences(xVar); assertThat(xRefs.isAssignedOnceInLifetime()).isFalse(); } public void testVarAssignedOnceInLifetime1() { testSame("var x = 0;"); ImmutableMap<String, Var> globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); Var xVar = globalVariableNamesMap.get("x"); assertThat(globalVariableNamesMap).containsKey("x"); ReferenceCollection xRefs = testedCollector.getReferences(xVar); assertThat(xRefs.isAssignedOnceInLifetime()).isTrue(); testSame("let x = 0;"); globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); xVar = globalVariableNamesMap.get("x"); assertThat(globalVariableNamesMap).containsKey("x"); xRefs = testedCollector.getReferences(xVar); assertThat(xRefs.isAssignedOnceInLifetime()).isTrue(); } public void testVarAssignedOnceInLifetime2() { testSame("{ var x = 0; }"); ImmutableMap<String, Var> globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); Var xVar = globalVariableNamesMap.get("x"); assertThat(globalVariableNamesMap).containsKey("x"); ReferenceCollection xRefs = testedCollector.getReferences(xVar); assertThat(xRefs.isAssignedOnceInLifetime()).isTrue(); } public void testBasicBlocks() { testSame(LINE_JOINER.join( "var x = 0;", "switch (x) {", " case 0:", " x;", "}")); ImmutableMap<String, Var> globalVariableNamesMap = testedCollector.getGlobalVariableNamesMap(); Var xVar = globalVariableNamesMap.get("x"); assertThat(globalVariableNamesMap).containsKey("x"); ReferenceCollection xRefs = testedCollector.getReferences(xVar); assertThat(xRefs.references).hasSize(3); assertNode(xRefs.references.get(0).getBasicBlock().getRoot()).hasType(Token.ROOT); assertNode(xRefs.references.get(1).getBasicBlock().getRoot()).hasType(Token.ROOT); assertNode(xRefs.references.get(2).getBasicBlock().getRoot()).hasType(Token.CASE); } }